如何为Ring/CLJ的file-response动态添加Content-Type响应头
解决Ring端点缺少Content-Type头的问题
你的Ring端点返回的响应缺少Content-Type头,确实会导致iOS/Safari无法正常播放媒体文件。以下两种方案可以解决这个问题:
方案一:使用Ring内置的wrap-content-type中间件
这个中间件会自动根据文件扩展名匹配并添加对应的Content-Type头,是最简便的方式。
应用到单个handler
直接把wrap-content-type包装在你的端点handler外层:
(require '[ring.middleware.content-type :refer [wrap-content-type]]) (def stream-partial-media {:summary "Stream partial media referenced by file-key" :parameters {:path {:file-key uuid?}} :handler (wrap-content-type (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))))))))})
全局应用(推荐)
如果多个端点都需要自动添加Content-Type,可在整个路由或应用外层包装中间件:
(require '[ring.adapter.jetty :as jetty]) (require '[ring.middleware.content-type :refer [wrap-content-type]]) ;; 假设your-routes是你的路由定义 (def app (-> your-routes wrap-content-type)) (jetty/run-jetty app {:port 3000})
自定义MIME类型映射
如果默认的MIME映射不符合需求(比如你需要将mp3映射为audio/mp3而非标准的audio/mpeg),可以传入:mime-types参数:
(wrap-content-type handler {:mime-types {"mp3" "audio/mp3" "mp4" "video/mp4"}})
方案二:手动添加Content-Type头
如果不想依赖中间件,可以手动解析文件扩展名并添加对应头:
- 先定义一个扩展名到MIME类型的映射函数:
(defn get-content-type [file-path] (let [ext (-> file-path (clojure.string/split #"\.") last clojure.string/lower-case)] (case ext "mp4" "video/mp4" "mp3" "audio/mp3" ;; 可添加更多扩展名的映射 "application/octet-stream")))
- 在handler中调用该函数,将Content-Type头加入响应:
(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)) file-path (utils/file-id-to-path (:file-id file-key-res)) content-type (get-content-type file-path)] (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的结果添加Content-Type头 (assoc (file-response file-path) :headers {"Content-Type" content-type})))))))
内容的提问来源于stack exchange,提问作者reynoldsnlp
相关产品推荐
相关产品推荐

