Handling File Uploads in Clojure Web Apps

Clojure web application development is still in it’s early stages and there aren’t lot of web applications written in Clojure. Recently I had to write add file uploading support to web application I’m developing in Clojure(using ring and compojure). I found following two articles by googling, but those lacks some of the basic internal details.

Rest of this post will discuss some of things I have discovered while implementing file upload support.

Below is the code for a simple compojure web application which implements file upload support.

(ns file-upload.core
  (:use [compojure.core]
        [ring.middleware.params]
        [ring.middleware.multipart-params]
        [ring.adapter.jetty]
        [hiccup.core]))

(defn home-page []
  (html [:form {:action "/file" :method "post" :enctype "multipart/form-data"}
         [:input {:name "file" :type "file" :size "20"}]
         [:input {:type "submit" :name "submit" :value "submit"}]]))

(defn upload-file [file]
  (let [file-name (file :filename)
        size (file :size)]
    (do
      (copy actual-file (File. (format "/Users/milinda/Desktop/%s" file-name)))
      {:status 200
       :headers {"Content-Type" "text/html"}
       :body (html [:h1 file-name]
                   [:h1 size])})))

(defroutes handler
  (POST "/file" {params :params}
       (let [file (get params "file")]
         (upload-file file)))
       (home-page)))

(def app
  (-> handler
      wrap-params
      wrap-multipart-params))

To add file uploading support, you need to wrap your requests using wrap-multipart-params middleware which can be found in ring.middleware.multipart-params namespace. Previous blog posts I mentioned uses clojure.contrib.duck-streams module. That module is deprecated now, instead of duck-streams you can use clojure.java.io.

According to my HTML form file input’s name is ‘file’. From input parameters you can get the ‘file’ field and it will be a map like following.

 {:size 88855, :tempfile #<File /var/folders/hq/ym6xf4794v7g4hvhll6nmr_c0000gn/T/ring-multipart-4366831661746413539.tmp>, :content-type application/pdf, :filename slides.pdf}

From that map you can get pointer to the actual file and pther attributes like file size anf file name. Above code shows how you can save uploaded file to anywahere you like.

Complete code can be found in github.

Moved from my old blog

 
63
Kudos
 
63
Kudos

Now read this

Versioning REST APIs

Yesterday, a discussion happened around versioning REST APIs resulted in an interesting sequence of events where one person even started to attack my self and one other person via personally e-mails. So I wanted to explore more about... Continue →