dakrone / clj-http

An idiomatic clojure http client wrapping the apache client. Officially supported version.
http://clojars.org/clj-http
MIT License
1.77k stars 408 forks source link

Allow overriding of the file name in multi part uploads #617

Open ahammel opened 2 years ago

ahammel commented 2 years ago

The Problem

I have to interact with a hateful API which expects a file uploaded as part of a multi-part field, where both the multi-part field and the file name have to be a specific value:

(let [file (io/file "foo.txt")]
  (client/post "https://hateful.io"
               {:multipart [{:name "special-snowflake", :content file}]}))
;; => 200

(let [file (io/file "foo.txt")]
  (client/post "https://hateful.io"
               {:multipart [{:name "something-else", :content file}]}))
;; => 400 "Expected multi-part field "special-snowflake" but didn't find it"

(let [file (io/file "foo.txt")
      other-file (io/file "bar.txt")]
  (io/copy file other-file)
  (client/post "https://hateful.io"
               {:multipart [{:name "special-snowflake", :content other-file}]}))
;; => 400 Expected file in the "special-snowflake" field to be called "foo.txt"

This is inconvenient in a server context, because I cannot safely use java.io.File/createTempFile, because it dynamically generates file names in order to avoid race conditions.

What I did instead

Something sad:


(defn- with-name-override
  [file file-name]
  (with-meta {:file file, :name file-name} {:type :file-with-name-override}))

(defmethod make-multipart-body :file-with-name-override
  [{:keys [name content]}]
  (if (and name content)
    (FileBody. (:file content)
               ContentType/APPLICATION_OCTET_STREAM
               (:name file-name))
    (throw
      (Exception.
        "Multipart :file-with-name-override must contain :name and :content"))))

(let [file (io/file "foo.txt")
      temp-file (with-name-override (File/createTempFile "foo" ".txt")
                                     "foo.txt")]
  (io/copy file (:file temp-file))
  (client/post "https://hateful.io"
               {:multipart [{:name "special-snowflake", :content temp-file}]}))

Proposed Enhancement

Allow a :file-name key in multipart field arguments, which overrides the name of the file if specified.