Rename file uploaded

291 views
Skip to first unread message

The Dude (Abides)

unread,
Jan 24, 2014, 3:31:42 AM1/24/14
to clo...@googlegroups.com
Hi, I'm new to clojure and am trying to rename an uploaded file with a unique identifier such as a long time stamp as follows:

(defn add-timestamp [filename]
 (let [ext-position (.lastIndexOf filename ".")
       timestamp    (tc/to-long (time/now))]
   (if (pos? ext-position)
     (str (.substring filename 0 ext-position)
          "-" timestamp (.substring filename ext-position))
     (str filename "-" timestamp))))

(defn handle-upload [filename]
 (upload-file (gallery-path) (add-timestamp filename))
 (resp/redirect "/upload"))

However the above throws an error as follows:

java.lang.IllegalArgumentException

No matching method found: lastIndexOf for class clojure.lang.PersistentArrayMap

Would appreciate any feedback on what I may be doing wrong in the add-timestamp function.

Jarrod Swart

unread,
Jan 24, 2014, 3:39:58 PM1/24/14
to clo...@googlegroups.com
It works for me.  I am guessing you are not passing in a string when you are calling (add-timstamp filename).  Which is to say that your param 'filename' is a map not a string.

If you are passing in a map of the upload information you need to grab the filename for your let:

(let [ext-postion (.lastIndexOf (:filename filename) ".")
  ...)

Hope that helps.

The Dude (Abides)

unread,
Jan 24, 2014, 11:28:00 PM1/24/14
to clo...@googlegroups.com
Hi Jarrod, I tried changing filename to string as follows

(defn handle-upload [filename]
 (upload-file (gallery-path) (add-timestamp (str filename)))
 (resp/redirect "/upload"))

and still got an error as:

java.lang.NullPointerException


My entire file code is:

(ns pgapp.routes.upload
  (:require [compojure.core :refer [defroutes GET POST]]
            [pgapp.views.layout :as layout]
            [noir.io :refer [upload-file resource-path]]
            [noir.session :as session]
            [noir.response :as resp]
            [noir.util.route :refer [restricted]]
            [clojure.java.io :as io]
            [ring.util.response :refer [file-response]]
            [taoensso.timbre :refer [error]]
            [pgapp.models.db :as db]
            [clj-time.core :as time]
            [clj-time.coerce :as tc]
            [pgapp.util
            :refer [galleries gallery-path thumb-uri thumb-prefix unique-prefix]])
   (:import [java.io File FileInputStream FileOutputStream]
                javax.imageio.ImageIO))

(use 'ring.middleware.params
        'ring.middleware.multipart-params)

(defn upload-page [info] 
  (layout/render "upload.html"))



(defn add-timestamp [filename]
 (let [ext-position (.lastIndexOf filename ".")
       timestamp    (tc/to-long (time/now))]
   (if (pos? ext-position)
     (str (.substring filename 0 ext-position)
          "-" timestamp (.substring filename ext-position))
     (str filename "-" timestamp))))

(defn handle-upload [filename]

 (upload-file (gallery-path) (add-timestamp (str filename)))
 (resp/redirect "/upload"))

(defroutes myapp-routes
  (GET "/upload" [info] (upload-page {:info info}))
  (POST "/upload" [file] (handle-upload file)))

I changed filename to (str filename) in the handle-upload function, but still no dice :(

The :refer [galleries gallery-path thumb-uri thumb-prefix unique-prefix]]) are just references to file paths in util.clj vs hardcoding them.

I got productive with clojure ref routes, sessions, views with selmer and queries either raw or with korma all easy peasy. However this one thing, this file upload issue has thrown me for a loop for 3 days now :) It is the bane of my existence right now :)

If I can get a handle on it, am considering creating a library to make this easier akin to file upload plugins in ruby world like carrier-wave. Thanks for any pointers on what I'm doing wrong with the above code.

Jarrod Swart

unread,
Jan 25, 2014, 12:05:35 AM1/25/14
to clo...@googlegroups.com
Well that isn't quite what I meant.  In that case you are just casting what is likely the map of upload data to a string.

Try this:

(defn handle-upload [filename]
   (str filename))

Why?  This will show you what type of data you receiving on upload.  My guess is that it is a map containing all the data about the file upload.


In short: you still don't have the right kind of data going into your function.  Change your handle-upload to the above and verify that you are getting the filename alone in your (let [filename (...)] ...) binding.

Hope that helps.

The Dude (Abides)

unread,
Jan 25, 2014, 12:36:45 AM1/25/14
to clo...@googlegroups.com
Hi Jarrod, you're exactly right, filename feeds the entire map as:

{:size 17401, :tempfile #, :content-type "image/jpeg", :filename "AngryBaby4.jpg"}

How can feed it just the :filename portion of the map as string to the add-timestamp function? Checking that link right now.

Jarrod Swart

unread,
Jan 25, 2014, 12:44:21 AM1/25/14
to clo...@googlegroups.com
You need to change your timestamp function to this, my change in bold:

(defn add-timestamp [filename]
 (let [ext-position (.lastIndexOf (:filename filename) ".")

       timestamp    (tc/to-long (time/now))]
   (if (pos? ext-position)
     (str (.substring filename 0 ext-position)
          "-" timestamp (.substring filename ext-position))
     (str filename "-" timestamp))))

Do you see why?  The original error you got was telling you that: .lasIndexOf is not a method you can call on (what was at the time) a clojure map.  Now you are extracting the filename from upload map by using the fact that maps are functions of their keys.  .lastIndexOf will now operate on a string successfully.

Best,
Jarrod

Jarrod Swart

unread,
Jan 25, 2014, 12:45:32 AM1/25/14
to clo...@googlegroups.com
I'm sorry I glossed over your code.  Leave (add-timestamp) the same but do this:

(defn handle-upload [filename]
 (upload-file (gallery-path) (add-timestamp (:filename filename)))
 (resp/redirect "/upload"))

The Dude (Abides)

unread,
Jan 25, 2014, 2:10:57 AM1/25/14
to clo...@googlegroups.com
Hi Jarrod, thanks for your help. I tried that but got a

java.lang.NullPointerException error again.

I tried the code without the add-timestamp and still get the

java.lang.NullPointerException

When I try it as just :filename filename it does in fact show only the file name.

So not sure why this is being so tricky, rest of clojure stuff has been very straightforward, this one file load issue is more boilerplate than I'm used to in ruby or coldfusion, and it just seems v tricky as the error codes are not yielding the source of the problem or maybe I don't yet know how to interpret them properly.

It doesn't seem the problem is in the "add-timestamp" function, as the operation croaks even before getting there when I isolate it to just 'handle-upload' function without ever calling the add-timestamp function.

I'll post the entire code again just in case you may spot where the error maybe. Perhaps the problem is in the libs I'm requiring or importing? Again thanks for taking a look, much appreciated.


(ns pgapp.routes.upload
  (:require [compojure.core :refer [defroutes GET POST]]
            [pgapp.views.layout :as layout]
            [noir.io :refer [upload-file resource-path]]
            [noir.session :as session]
            [noir.response :as resp]
            [noir.util.route :refer [restricted]]
            [clojure.java.io :as io]
            [ring.util.response :refer [file-response]]
            [taoensso.timbre :refer [error]]
            [pgapp.models.db :as db]
            [clj-time.core :as time]
            [clj-time.coerce :as tc]
            [pgapp.util
            :refer [galleries gallery-path thumb-uri thumb-prefix unique-prefix]])
   (:import [java.io File FileInputStream FileOutputStream]
            javax.imageio.ImageIO))

(defn upload-page [info]  
  (layout/render "upload.html"))

(defn add-timestamp [filename]
 (let [ext-position (.lastIndexOf filename ".")
       timestamp    (tc/to-long (time/now))]
   (if (pos? ext-position)
     (str (.substring filename 0 ext-position)
          "-" timestamp (.substring filename ext-position))
     (str filename "-" timestamp))))

(defn handle-upload [filename]
 (upload-file (gallery-path) (add-timestamp (:filename filename)))
 (resp/redirect "/upload"))

(defroutes upload-routes

Jarrod Swart

unread,
Jan 25, 2014, 2:10:56 PM1/25/14
to clo...@googlegroups.com
Nothing sticks out, but a NullPointerException is something I most commonly get as a result of an incorrect file path.  Things I would do:

1. Make sure that (gallery-path) actually exists or is writable.
2. I would do (POST "/upload" [file] (println file) (handle-upload file))), to inspect what you are capturing on the route destructure.

Again that NullPointerException means that the code is expecting some object, but you are passing in null.  As I stated 99% of the time I get that error in Clojure it is due to an improper path/filename/resource.  So I would start checking any part of the code that reads/writes to files on the hard drive.  Make sure they have the proper permissions, and that the paths look write.

I'm sure this seems like stuff you know or may have tried, but I found when getting started with Clojure that everything was so new in general that it broke my ability to "debug".  I had to learn how to troubleshoot all over again.

Good luck!

The Dude (Abides)

unread,
Jan 25, 2014, 8:51:42 PM1/25/14
to clo...@googlegroups.com
Thanks for the feedback Jarrod, the gallery-path did exist because I was able to upload files without renaming them and I saw the map with keys per your tip. I just wasn't familiar enough with the plumbing in noir.io or the language as I finally had the time to spend a week getting into clojure more seriously. Took a look in more depth in noir.io, surprisingly concise and simple to understand. With some much appreciated help finally got it working as follows:


(ns pgapp.routes.upload
  (:require [compojure.core :refer [defroutes GET POST]]
            [pgapp.views.layout :as layout]
            [noir.io :refer [upload-file resource-path]]
            [noir.session :as session]
            [noir.response :as resp]
            [noir.util.route :refer [restricted]]
            [ring.util.response :refer [file-response]]
            [taoensso.timbre :refer [error]]
            [pgapp.models.db :as db]
            [clj-time.core :as time]
            [clj-time.coerce :as tc]
            [pgapp.util
            :refer [galleries gallery-path thumb-uri thumb-prefix unique-prefix]]))


(defn upload-page [info] 
  (layout/render "upload.html"))

(defn add-timestamp [filename]
 (let [ext-position (.lastIndexOf filename ".")
       timestamp    (tc/to-long (time/now))]
   (if (pos? ext-position)
     (str (.substring filename 0 ext-position)
          "-" timestamp (.substring filename ext-position))
     (str filename "-" timestamp))))

(defn handle-upload [file]
  (upload-file (gallery-path) (update-in file [:filename] add-timestamp))

  (resp/redirect "/upload"))

(defroutes upload-routes
  (GET "/upload" [info] (upload-page {:info info}))
  (POST "/upload" [file] (handle-upload file)))


The key to it was the 2nd line in handle-upload as:

(upload-file (gallery-path) (update-in file [:filename] add-timestamp))

Jarrod Swart

unread,
Jan 25, 2014, 10:29:34 PM1/25/14
to clo...@googlegroups.com
Awesome, glad you got it working.  I highly recommend: http://pragprog.com/book/dswdcloj/web-development-with-clojure, for step by step Web Dev in Clojure.  Reading through it got me up to speed really quickly as far as project structure, common libs and practices, etc.
Reply all
Reply to author
Forward
0 new messages