Permalink
Browse files

Added multipart form middleware

  • Loading branch information...
1 parent 154f5fe commit 2f4e5ae5b8b6afe870f95d1de9e61610d76a5834 @weavejester weavejester committed Feb 21, 2010
Showing with 88 additions and 8 deletions.
  1. +2 −0 project.clj
  2. +78 −0 src/ring/middleware/multipart.clj
  3. +8 −8 src/ring/middleware/params.clj
View
2 project.clj
@@ -9,6 +9,8 @@
[org.apache.httpcomponents/httpcore "4.0.1"]
[org.apache.httpcomponents/httpcore-nio "4.0.1"]
[commons-codec "1.4"]
+ [commons-fileupload "1.2.1"]
+ [commons-io "1.4"]
[clj-html "0.1.0-SNAPSHOT"]
[clj-stacktrace "0.1.0-SNAPSHOT"]]
:repositories [["mvnrepository" "http://mvnrepository.com/"]
View
78 src/ring/middleware/multipart.clj
@@ -0,0 +1,78 @@
+(ns ring.middleware.multipart
+ (:use clojure.contrib.def
+ [ring.middleware.params :only (assoc-param)])
+ (: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 encoding]
+ (proxy [RequestContext] []
+ (getContentType [] (:content-type request))
+ (getContentLength [] (:content-length request))
+ (getCharacterEncoding [] encoding)
+ (getInputStream [] (:body request))))
+
+(defn- file-map
+ "Create a file map from a DiskFileItem."
+ [#^DiskFileItem item]
+ (with-meta
+ {:filename (.getName item)
+ :size (.getSize item)
+ :content-type (.getContentType item)
+ :tempfile (.getStoreLocation item)}
+ {:disk-file-item item}))
+
+(defn parse-multipart-params
+ "Parse a map of multipart parameters from the request."
+ [request encoding]
+ (reduce
+ (fn [param-map, #^DiskFileItem item]
+ (assoc-param param-map
+ (.getFieldName item)
+ (if (.isFormField item)
+ (.getString item)
+ (file-map item))))
+ {}
+ (.parseRequest
+ file-upload
+ (request-context request encoding))))
+
+(defn wrap-multipart
+ "Middleware to parse multipart parameters from a request. Adds the
+ following keys to the request map:
+ :multipart-params - a map of multipart parameters
+ :params - a merged map of all types of parameter
+ Takes an optional configuration map. Recognized keys are:
+ :encoding - character encoding to use for multipart parsing. If not
+ specified, uses the request character encoding, or \"UTF-8\"
+ if no request character encoding is set."
+ [handler & [opts]]
+ (fn [request]
+ (let [encoding (or (:encoding opts)
+ (:character-encoding request)
+ "UTF-8")
+ params (if (multipart-form? request)
+ (parse-multipart-params request encoding)
+ {})
+ request (merge-with merge request
+ {:multipart-params params}
+ {:params params})]
+ (handler request))))
View
16 src/ring/middleware/params.clj
@@ -4,12 +4,7 @@
(:use clojure.contrib.str-utils)
(:import java.net.URLDecoder))
-(defn- urldecode
- "Decode a urlencoded string using the specified encoding."
- [text encoding]
- (URLDecoder/decode text encoding))
-
-(defn- assoc-vec
+(defn assoc-param
"Associate a key with a value. If the key already exists in the map,
create a vector of values."
[map key val]
@@ -20,13 +15,18 @@
[cur val])
val)))
+(defn- urldecode
+ "Decode a urlencoded string using the specified encoding."
+ [text encoding]
+ (URLDecoder/decode text encoding))
+
(defn- parse-params
"Parse parameters from a string into a map."
[#^String param-string encoding]
(reduce
(fn [param-map encoded-param]
(if-let [[_ key val] (re-matches #"([^=]+)=(.*)" encoded-param)]
- (assoc-vec param-map
+ (assoc-param param-map
(urldecode key encoding)
(urldecode (or val "") encoding))
param-map))
@@ -61,7 +61,7 @@
the request map:
:query-params - a map of parameters from the query string
:form-params - a map of parameters from the body
- :params - a merged map of both query and form parameters
+ :params - a merged map of all types of parameter
Takes an optional configuration map. Recognized keys are:
:encoding - encoding to use for url-decoding. If not specified, uses
the request character encoding, or \"UTF-8\" if no request

0 comments on commit 2f4e5ae

Please sign in to comment.