File uploads with Clojure, Ring and Compojure

I just found a great write up on how to upload files by Nikola Plejić. It’s a little bit sparse though so below I’ll revisit it with my own additions.

Let’s start out with the Compojure route in question:

(wrap-multipart-params
   (POST "/addbanner*" req
         (binding [tutorial/*req* req]
           (tutorial/add-banner (req :multipart-params)))))

As you can see we work with the whole request map here and first bind it to be able to later work with various parameters in addition to the form data (in my case validation of data in the query parameters).

(defn add-banner [params]
  (let [merchant (login-merchant) banner (get params "banner")]
    (if (or (is-empty? (banner :filename)) (nil? merchant))
      "no merchant or empty file"
     (let [new-name (str (java.util.UUID/randomUUID) "."  (last (string/split (banner :filename) #"\.")))]
       (ds/copy (banner :tempfile) (ds/file-str (str "banners/" new-name)))
       (db/insert-banner merchant new-name)))))

So the goal here is for a merchant to upload a banner that his affiliates can later use, instead of file my file field is named banner. Login-merchant is using the bound req above to fetch the merchant in question. We fetch the file name and keep the file name extension, string refers to clojure.contrib.str-utils2. We create a new unique name for the file with Java’s UUID utility which we concatenate to the extension, this is the new complete file name.

The ds here refers to clojure.contrib.duck-streams, just like in Nikola’s tutorial.

Finally we insert the banner into the database (MongoDB by way of CongoMongo but that is a completely different story).


Related Posts

Tags: , , ,