clj-http
clj-http copied to clipboard
Allow overriding of the file name in multi part uploads
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.