summaryrefslogtreecommitdiff
path: root/compojure-3.2/src/compojure/http/multipart.clj
blob: afd6737ac85ef9abe6d801362748f430f4553119 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
;; Copyright (c) James Reeves. All rights reserved.
;; The use and distribution terms for this software are covered by the Eclipse
;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which
;; can be found in the file epl-v10.html at the root of this distribution. By
;; using this software in any fashion, you are agreeing to be bound by the
;; terms of this license. You must not remove this notice, or any other, from
;; this software.

(ns compojure.http.multipart
  "Add multipart form handling to Compojure. Relies on the Apache Commons
   FileUpload library."
  (:use clojure.contrib.def
        compojure.map-utils)
  (:import [org.apache.commons.fileupload FileUpload RequestContext]
           [org.apache.commons.fileupload.disk DiskFileItemFactory DiskFileItem]))

(defn multipart-form?
  "Does a request have a multipart form?"
  [request]
  (if-let [content-type (:content-type request)]
    (.startsWith content-type "multipart/form-data")))

(defvar- file-upload
  (FileUpload.
    (doto (DiskFileItemFactory.)
      (.setSizeThreshold -1)
      (.setFileCleaningTracker nil)))
  "Uploader class to save multipart form values to temporary files.")

(defn- request-context
  "Create a RequestContext object from a request map."
  [request]
  (proxy [RequestContext] []
    (getContentType []       (:content-type request))
    (getContentLength []     (:content-length request))
    (getCharacterEncoding [] (:character-encoding request))
    (getInputStream []       (:body request))))

(defn- file-map
  "Create a file map from a DiskFileItem."
  [#^DiskFileItem item]
  {:disk-file-item item
   :filename       (.getName item)
   :size           (.getSize item)
   :content-type   (.getContentType item)
   :tempfile       (.getStoreLocation item)})

(defn parse-multipart-params
  "Parse a map of multipart parameters from the request."
  [request]
  (reduce
    (fn [param-map, #^DiskFileItem item]
      (assoc-vec param-map
        (keyword (.getFieldName item))
        (if (.isFormField item)
          (if (zero? (.getSize item))
            ""
            (.getString item))
          (file-map item))))
    {}
    (.parseRequest
       file-upload
       (request-context request))))

(defn get-multipart-params
  "Retrieve multipart params from the request."
  [request]
  (if (multipart-form? request)
    (parse-multipart-params request)
    {}))

(defn with-multipart
  "Decorate a Ring handler with multipart parameters."
  [handler]
  (fn [request]
    (let [params  (get-multipart-params request)
          request (-> request
                    (assoc :multipart-params params)
                    (assoc :params (merge (request :params) params)))]
      (handler request))))