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

 
66
Kudos
 
66
Kudos

Now read this

Web Application Development in Clojure

I did some coding in Scheme and Ruby last couple of months and felt that writing Java code is not fun anymore. But Java as a platform is really good for most of the tasks and I started to learn some Clojure because there is support for... Continue →