How to send an input stream as a response in ring?

652 Views Asked by At

I have the following code, in which I want to send an InputStream of a file in the function fetch-items, which handles the route /fetch-items.

(defn id->image [image-id]
    (let [image (.getInputStream (gfs/find-by-id fs image-id))] image))

(defn item-resp [item]
  (assoc item :_id (str (:_id item))
         :images (into [] (map id->image (:image-ids item))))
  )

(defn fetch-items [req]
  (res/response 
   (map item-resp (find fs "items" {}))))

Here's my request in the client side, using cljs-ajax:

   (ajax-request
    {:uri "http://localhost:5000/fetch-items"
     :method :get
     :handler #(prn (into [] %))
     :format (json-request-format)
     :response-format (raw-response-format)
     }
    )

But the response I get on the client is this:

[:failure :parse] [:response nil] [:status-text "No reader function for tag object.  Format should have been EDN"]
:original-text "{:_id \"5e63f5c591585c30985793cd\", :images [#object[com.mongodb.gridfs.GridFSDBFile$GridFSInputStream 0x22556652 \"com.mongodb.gridfs.GridFSDBFile$GridFSInputStream@22556652\"]]}{:_id \"5e63f5d891585c30985793d0\", :images [#object[com.mongodb.gridfs.GridFSDBFile$GridFSInputStream 0x266ae6c0 \"com.mongodb.gridfs.GridFSDBFile$GridFSInputStream@266ae6c0\"]]}{:_id \"5e63f5e891585c30985793d3\", ...

Why would the response say that the format should have been edn? How do I extract this file/image out in the client side?

--- EDIT ----

Doing the following:

(IOUtils/toString image "utf-8")

returns a string of size 1594 bytes, which is much smaller than the expected image size. I think this is because it's converting the file object to base64 and not the actual chunk of data associated with it. database instance

How do I make it convert the actual GridFS chunk to base64 string and not the file object?

1

There are 1 best solutions below

6
Denis Fuenzalida On

It seems that you are building a response and directly putting an reference to an InputStream object into the response, without encoding the contents of the stream into an array of bytes and serializing the contents on the response.

You'll need to find a way to read the contents of the stream and encode it in the response (maybe send them encoded as base 64?)

On the other end, the client seems to be expecting an EDN response, and when it found the string #object, it complained that it didn't have a way to read an object with such a tag.

Here's a simple example of how to read an EDN string with a tagged literal, you can extend it so you decode the image in the client (note I'm using Java in the decoder, you'll need a different implementation on JS):

(defn b64decode [s]
  (->> s .getBytes (.decode (java.util.Base64/getDecoder)) String.))

(def message "{:hello :world :msg #base64str \"SGV5LCBpdCB3b3JrcyE=\"}")

;; Now we can read the EDN string above adding our handler for #base64str

(clojure.edn/read-string {:readers {'base64str b64decode}} message)
;; => {:hello :world, :msg "Hey, it works!"}