-
-
Save mccraigmccraig/cc97ae2f0fde1e0bbda7b51e1a0001dd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns er-api.service.resources.tempfile | |
(:require | |
;; [taoensso.timbre :refer [debug info warn error]] | |
[clojure.tools.logging :refer :all] | |
[schema.core :as s] | |
[clojure.java.io :as io] | |
[cheshire.core :as json] | |
[manifold.stream :as stream] | |
[byte-streams :as bs] | |
[yada.yada :refer [resource]] | |
[yada.request-body :as req-body] | |
[yada.multipart :as mp] | |
[bidi.ring :as bidi] | |
[aleph.http :as http] | |
[ring.swagger.json-schema :as rjs]) | |
(:import [java.io File] | |
[ring.swagger.json_schema])) | |
(defrecord TempfilePartial [part fieldname filename content-type f out] | |
mp/Partial | |
(continue [this piece] | |
(info "continue TempfilePartial" | |
{:fieldname fieldname | |
:filename filename | |
:content-type content-type | |
:tempfile f | |
:piece piece}) | |
(.write out (:bytes piece)) | |
this) | |
(complete [this state piece] | |
(when piece | |
(info "complete TempfilePartial" | |
{:fieldname fieldname | |
:filename filename | |
:content-type content-type | |
:tempfile f | |
:piece piece}) | |
(.write out (:bytes piece))) | |
(.close out) | |
(update state | |
:parts | |
(fnil conj []) | |
(-> part | |
(assoc :tempfile-partial this))))) | |
(defmethod rjs/convert-class TempfilePartial [_ _] {:type "string"}) | |
(defn create-tempfile-partial | |
[{:keys [headers bytes body-offset] :as piece}] | |
(let [cd (get headers "content-disposition") | |
[_ fieldname] (some->> cd (re-find #"name=\"([^\"]+)\"")) | |
[_ filename] (some->> cd (re-find #"filename=\"([^\"]+)\"")) | |
[_ pre suff] (some->> filename (re-find #"(.+)\.(.*)")) | |
pre (cond | |
(and pre (<= 3 (count pre))) pre | |
pre (str pre "___") | |
:default pre) | |
ct (get headers "content-type") | |
f (File/createTempFile (or pre "file") (when suff (str "." suff))) | |
out (io/output-stream f)] | |
(info "create-tempfile-partial" | |
{:fieldname fieldname | |
:filename filename | |
:content-type ct | |
:tempfile f | |
:piece piece}) | |
(.write out | |
bytes | |
body-offset | |
(- (alength bytes) body-offset)) | |
(->TempfilePartial (-> piece | |
(dissoc :bytes) | |
(assoc :type :part)) | |
fieldname | |
filename | |
ct | |
f | |
out))) | |
(defn single-piece-tempfile-partial | |
[state {:keys [] :as part}] | |
(let [{:keys [fieldname filename content-type f] :as tp} (create-tempfile-partial part)] | |
(info "single-piece TempfilePartial" | |
{:fieldname fieldname | |
:filename filename | |
:content-type content-type | |
:tempfile f | |
:part part}) | |
(mp/complete tp state nil))) | |
(defrecord TempfilePartConsumer [] | |
mp/PartConsumer | |
(consume-part [_ state part] | |
(info "consume-part" part) | |
(single-piece-tempfile-partial state part)) | |
(start-partial [_ piece] | |
(info "start-partial" piece) | |
(create-tempfile-partial piece))) | |
(def tempfile-part-coercion-matchers | |
{String | |
(fn [part] | |
(cond | |
(instance? yada.multipart.DefaultPart part) | |
(let [offset (get part :body-offset 0)] | |
(String. (:bytes part) | |
offset | |
(- (count (:bytes part)) offset))) | |
(and | |
(:tempfile-partial part) | |
(instance? er_api.service.resources.tempfile.TempfilePartial | |
(:tempfile-partial part))) | |
(let [tfp (:tempfile-partial part)] | |
(slurp (:f tfp))) | |
:else | |
(throw (ex-info "can't coerce part to String" {:part part})))) | |
er_api.service.resources.tempfile.TempfilePartial | |
:tempfile-partial}) | |
(defn tempfile-resource | |
[] | |
(resource | |
{:methods {:post {:consumes #{"multipart/form-data"} | |
:part-consumer (->TempfilePartConsumer) | |
:produces #{"application/json"} | |
:response (fn [ctx] (json/generate-string :ok))}} | |
:access-control {:allow-origin #{"*"} | |
:allow-headers ["Authorization" "Content-Type"] | |
:allow-methods #{:post :get}}})) | |
(defn make-handler | |
[app] | |
(bidi/make-handler ["/" (tempfile-resource)])) | |
(defn start-server | |
[port] | |
(http/start-server (make-handler {}) | |
{:port port | |
:raw-stream? true})) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment