Search code examples
fileclojureuploadresponse

Clojure File Response


I am currently building a Clojure database-backed website on a Luminus +h2 framework. I am working on file uploading currently, and stuck at the point of actually retrieving the files. Though I'm not completely sure this was the best way to approach the situation, here is what I have done so far:

I have a route that allows me to upload files. The files that get uploaded are copied to the /resources/public/Files folder inside of my project:

(io/copy actual-file (io/file "resources" "public" "Files" file-name))

Furthermore, I store the file name of each of the files inside of a database table that is created and manipulated using SQL.

I then have a new route which shows all of the files that have been uploaded as links (by accessing the database). In the back end, the links direct the page to a new route "/file/:file-name" in which the file-response function is called. I'm hoping that the links will work as "downloads" for the files.

As my first attempt at this working, I copied all the files to my C:/AllFiles folder, and did this:

(defn serve-file [file-name]
  (file-response (str (files-path) File/separator file-name)))

where files-path is:

(defn files-path [] "/AllFiles")

This actually was working for me. However, I want the file that I'm using to be the one from inside my specific project directory, without me having to type the whole path (i.e. so that it works for anyone using it, at ~/Project-Name/resources/public/Files").

For some reason, I can't seem to get file-response to work like such.

Thanks for your help.


Solution

  • ok so a couple ideas (i'm not sure what will suit your situation best):

    • you can get the current working directory of the process like so:

    (System/getProperty "user.dir")

    • you can change the current working directory like so:

    (System/getProperty "user.dir" "/users/lispHK01")

    So you could potentially do something like this:

    (def initial-working-path (System/getProperty "user.dir"))
    
    (def my-relative-files-path "foo/bar/wherefileswillbe")
    
    (def files-path
      (str
        initial-working-path
        File/separator
        my-relative-files-path))
    

    If you are needing to update files-path multiple times, you can use an atom to do so (though, standard Clojure 'best practices' disclaimer: try to look for a functional/immutable approach prior to relying on an atom). An atom is derefed by prefacing with @, e.g.:

    user=> (def foo (atom "my/path"))
    #'user/foo
    user=> @foo
    "my/path"
    user=> (reset! foo "my/new/path")
    "my/new/path"
    user=> @foo
    "my/new/path"
    user=>