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

 
58
Kudos
 
58
Kudos

Now read this

Interesting Resources on Writing

Starting to participate in #The100DayProject by writing every day for 100 days got me into research more about writing. Writing is major part of the life as grad student. But I was far behind my writing and I wanted to improve by writing... Continue →