I have the following endpoint handler (using clj
/ring
), and it works fine, but it doesn't include a Content Type
header in the response, which might be a reason why the returned video does not play in iOS/Safari.
(def stream-partial-media
{:summary "Stream partial media referenced by file-key"
:parameters {:path {:file-key uuid?}}
:handler (fn [{{{:keys [file-key]} :path} :parameters}]
(let [file-key-res (file-keys/READ-UNEXPIRED file-key)]
(if (nil? file-key-res)
{:status 404
:body {:message "file-key not found"}}
(let [user-res (users/READ (:user-id file-key-res))]
(if (or (:dev env) (:prod env))
(log-ut/log-media-access {:file-id (str (:file-id file-key-res))
:username (:username user-res)}))
(file-response (utils/file-id-to-path (:file-id file-key-res)))))))})
I want to make the endpoint dynamically return a Content Type
, based on the extension of the requested file (mp4
/mp3
/etc.). I found that ring
has a built in wrap-content-type
function that uses the file's extension to add a Content Type
header, but I do not know where to implement it. I tried putting it in a few different places, but still have no Content Type
header.
I would like to know either how to correct implement this wrapper, or how to check the file extension and manually add a header on that basis: mp4
=>video/mp4
, mp3
=>audio/mp3
.
From the request you have to get the filename, from which you can get the extension. Then based on the extension you can add the "Content-Disposition"
to the headers map. Note that Ring request is just a map and the response as well. Something like this might work:
(require '[ring.util.response :refer [header]])
(defn extension [s]
(second (re-find #"\.([a-zA-Z0-9]+)$" s)))
(extension "foo.mp3")
;; => "mp3"
(extension "foo.mp4")
;; => "mp4"
(extension "foo")
;; => nil
(def stream-partial-media
{:summary "Stream partial media referenced by file-key"
:parameters {:path {:file-key uuid?}}
:handler (fn [{{{:keys [file-key]} :path} :parameters :as request}]
(let [file-key-res (file-keys/READ-UNEXPIRED file-key)]
(if (nil? file-key-res)
{:status 404
:body {:message "file-key not found"}}
(let [user-res (users/READ (:user-id file-key-res))]
(when (or (:dev env) (:prod env))
(log-ut/log-media-access {:file-id (str (:file-id file-key-res))
:username (:username user-res)}))
(-> (file-response (utils/file-id-to-path (:file-id file-key-res)))
(header "Content-Type"
(case (extension (:filename request))
:mp4 "video/mp4"
:mp3 "audio/mp3")))))))})
Above Ring's (header "Content-Type" <value>)
just adds {:header {"Content-Type" <value>}
to the response. The assumption is the :filename
key is available in the request
.
I would also add some extra check to see if the filename is correct, and otherwise return an error.