Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read a binary file's contents in to a string in Clojure?

Tags:

clojure

I'm creating a static file server in Clojure with Compojure and I'm stuck on reading an image from the filesystem and displaying that image through a Compojure route.

slurp unfortunately doesn't handle binary data very well, and I've tried this 100 different ways since, but this is my latest failed attempt:

(defn image-output [filepath]
  (try
    (let [contents (apply str (with-open [rdr (io/reader filepath)]
      (into #{} (line-seq rdr))))]
      {
        :status 200
        :headers 
        {
          "Content-Type" "image/jpeg", 
          "Content-Length" "",
          "Cache-Control" "",
          "Expires" ""
        }
        :body contents
      }))
    (catch Exception e {:status 404})))

(defn endpoint_view [params]
  (if (contains? params :bucket)
    (image-output (join "/" [data_path (:bucket params) (:dir params) (:filename params)]))))

(defroutes main-routes
  (GET "/view/:bucket/:dir/:filename" {params :params} (endpoint_view params))
  (route/files "/")
  (route/resources "/s" {:root "./public/s"})
  (route/not-found "Page not found"))

It seems this current attempt suffers the same fate as using slurp, where I can echo the contents string and it's and encoded string, but when I change the content-type to image/jpeg it's a broken image.

I spent all day yesterday Google searching, but none of the examples accomplished the same goal, and while they helped me understand a little more about Java IO, they weren't clear enough to help me get where I needed to go, or produced the same results I was already getting (example: Best way to read contents of file into a set in Clojure).

(Imaginary bonus points if you can tell me how to get the content type from the filepath as well as that's my next question!)

like image 976
Mike Flynn Avatar asked Jul 06 '12 19:07

Mike Flynn


1 Answers

Just make the body be (io/file filepath) - Ring is perfectly happy to serve files for you.

Edit for bonus points: you can use ring.middleware.file-info/wrap-file-info to get file metadata for the files you return. Or, you could just serve a whole directory with (compojure.route/files "/public"), which does all this mess for you.

like image 61
amalloy Avatar answered Sep 22 '22 12:09

amalloy