|
5 | 5 | ;; using this software in any fashion, you are agreeing to be bound by the |
6 | 6 | ;; terms of this license. You must not remove this notice, or any other, from |
7 | 7 | ;; this software. |
8 | | -;; Modified by Adam Blinkinsop <blinks@acm.org> in August 2009. |
9 | 8 |
|
10 | 9 | (ns compojure.http.multipart |
11 | 10 | "Add multipart form handling to Compojure. Relies on the Apache Commons |
12 | 11 | FileUpload library." |
13 | 12 | (:use clojure.contrib.def) |
14 | 13 | (:use compojure.map-utils) |
15 | | - (:import [org.apache.commons.fileupload.servlet ServletFileUpload])) |
| 14 | + (:import org.apache.commons.fileupload.FileUpload) |
| 15 | + (:import org.apache.commons.fileupload.RequestContext) |
| 16 | + (:import org.apache.commons.fileupload.disk.DiskFileItemFactory) |
| 17 | + (:import org.apache.commons.fileupload.disk.DiskFileItem)) |
16 | 18 |
|
17 | | -(defn multipart? |
18 | | - "Does this request contain multipart content?" |
| 19 | +(defn multipart-form? |
| 20 | + "Does a request have a multipart form?" |
19 | 21 | [request] |
20 | | - (ServletFileUpload/isMultipartContent request)) |
| 22 | + (if-let [content-type (:content-type request)] |
| 23 | + (.startsWith content-type "multipart/form-data"))) |
21 | 24 |
|
22 | | -(defvar- upload (ServletFileUpload.)) |
| 25 | +(defvar- file-upload |
| 26 | + (FileUpload. |
| 27 | + (doto (DiskFileItemFactory.) |
| 28 | + (.setSizeThreshold -1) |
| 29 | + (.setFileCleaningTracker nil))) |
| 30 | + "Uploader class to save multipart form values to temporary files.") |
23 | 31 |
|
24 | | -(defn field-seq |
25 | | - "Map field names to values, which will either be a simple string or map. |
| 32 | +(defn- request-context |
| 33 | + "Create a RequestContext object from a request map." |
| 34 | + [request] |
| 35 | + (proxy [RequestContext] [] |
| 36 | + (getContentType [] (:content-type request)) |
| 37 | + (getContentLength [] (:content-length request)) |
| 38 | + (getCharacterEncoding [] (:character-encoding request)) |
| 39 | + (getInputStream [] (:body request)))) |
| 40 | + |
| 41 | +(defn- file-map |
| 42 | + "Create a file map from a DiskFileItem." |
| 43 | + [#^DiskFileItem item] |
| 44 | + {:disk-file-item item |
| 45 | + :filename (.getName item) |
| 46 | + :size (.getSize item) |
| 47 | + :content-type (.getContentType item) |
| 48 | + :tempfile (.getStoreLocation item)}) |
| 49 | + |
| 50 | +(defn parse-multipart-params |
| 51 | + "Parse a map of multipart parameters from the request." |
| 52 | + [request] |
| 53 | + (reduce |
| 54 | + (fn [param-map, #^DiskFileItem item] |
| 55 | + (assoc-vec param-map |
| 56 | + (keyword (.getFieldName item)) |
| 57 | + (if (.isFormField item) |
| 58 | + (if (zero? (.getSize item)) |
| 59 | + "" |
| 60 | + (.getString item)) |
| 61 | + (file-map item)))) |
| 62 | + {} |
| 63 | + (.parseRequest |
| 64 | + file-upload |
| 65 | + (request-context request)))) |
26 | 66 |
|
27 | | - Multipart values will be maps with content-type, name (original filename), |
28 | | - and stream (an open input stream object)." |
| 67 | +(defn get-multipart-params |
| 68 | + "Retrieve multipart params from the request." |
29 | 69 | [request] |
30 | | - (map (fn [i] {(keyword (.getFieldName i)) |
31 | | - (if (.isFormField i) |
32 | | - (.getParameter request (.getFieldName i)) |
33 | | - {:content-type (.getContentType i) |
34 | | - :name (.getName i) |
35 | | - :stream (.openStream i)})}) |
36 | | - (.getItemIterator upload request))) |
| 70 | + (if (multipart-form? request) |
| 71 | + (parse-multipart-params request) |
| 72 | + {})) |
37 | 73 |
|
38 | 74 | (defn with-multipart |
| 75 | + "Decorate a Ring handler with multipart parameters." |
39 | 76 | [handler] |
40 | 77 | (fn [request] |
41 | | - (let [req (merge request {:params (merge (field-seq request))})] |
| 78 | + (let [params (get-multipart-params request) |
| 79 | + request (-> request |
| 80 | + (assoc :multipart-params params) |
| 81 | + (assoc :params (merge (request :params) params)))] |
42 | 82 | (handler request)))) |
0 commit comments