简体   繁体   中英

Serving binary files from the database with compojure

I have the following routes definition:

(require '[compojure.core :as ccore]
         '[ring.util.response :as response])

(def *main-routes*
     (ccore/defroutes avalanche-routes
       (ccore/GET "/" [] "Hello World 2")
       (ccore/GET "/images/:id" [id] (get-image-response id))))

In this example, requesting / works like a charm and returns the expected Hello World 2 .

The get-images-response method is defined like this:

(defn get-image-response
  [id]
  (let [record (db/get-image id false)]
    (-> (response/response (:data record))
        (response/content-type (:content-type record))
        (response/header "Content-Length" (:size record)))))

I get a 404 though, so the serving of binary files doesn't quite work yet. Any ideas why?

Edit: Ok, the issue relates to the fact that images are being requested on /images/name.jpg . As soon as I remove the .jpg the handler gets called. So the question becomes how do I match on anything but the extension?

Compojure uses clout for route matching. The dot character has a special meaning in clout routes. It represents a token separator, similarly to the slash character. The following characters all have this meaning in clout: / . , ; ? / . , ; ? .

That means that a route like "/images/:id" will not match a uri of the form /images/name.jpg since images , name and jpg each represent a separate token in clout.

In order to match it, you could compose your route in a number of different ways, depending on your need.

If all your images have the .jpg extension, the easiest thing to do would be:

(GET "/images/:id.jpg" [id] ...)

If the extension varies you could do the following:

(GET "/images/:name.:extension" [name extension] ...)

If you want to restrict the extension, you can pass compojure/clout a regular expression:

(GET ["/images/:name.:ext", :ext #"(jpe?g|png|gif)"] [name ext] ...)

You could also go with a wildcard, which is less precise and would match any uri starting with /images/ :

(GET "/images/*" [*] ...)

The real answer in this case was that there was a bug in the clojure-couchdb library. The patch is available on github here .

It boils down to adding the {:as :byte-array} map parameter and value to the request sent via clj-http to couch's api.

The other issue in my code was that ring doesn't really know what to do with byte-arrays when it's rendering them. Rather than patching ring, I just wrapped the byte-array into a java.io.ByteArrayInputStream . Here is the complete code for handling the download:

(defn get-image-response
  [id]
  (let [record (db/get-image id false)]
    (-> (response/response (new java.io.ByteArrayInputStream (:data record)))
        (response/content-type (:content-type (:content-type record)))
        (response/header "Content-Length" (:size record)))))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM