How to encode and return files via api endpoint

I have images that I would like to return from a postgresql database or directory that have been uploaded by a user (think profile photo, attachments etc). How do I properly encode the image (whether as a byte array or json) using Clojure to send it back to the front end from the db, and then decode the image properly using ClojureScript?

My current tech stack is based off of re-frame.core for subs and events, and [day8.re-frame.http-fx] for making and sending requests. For testing purposes, I have tried to simply create a route to serve up a static image located at “/resources/private/images/myimage.jpg” from the backend to the frontend, but I keep running into an error where it says it can’t properly JSON encode the image. My code can turn the image into a File or URL object on the backend, but I don’t know how to encode it properly to send it to the front and the decode it. I’ve tried byte/char arrays and using an output stream, but haven’t had much success there yet.

The following is work-in-progress (eg not fully functioning) code in a backend clojure file for serving an image located on the file path. Any tips or suggestions would be appreciated.

(ns app-name.media.images
  (:require [ring.util.response :as r]
            [ring.util.http-response :as response]            
            [clojure.java.io :as io]))

(defn request-image
  ""
  [image]
  (let [file (io/file (str "private/images/" image))
        byteArray (byte-array (.length file))]
    byteArray))                           

(defn slurp-bytes
  "Slurp the bytes from a slurpable thing"
  [x]
  (with-open [out (java.io.ByteArrayOutputStream.)]
    (clojure.java.io/copy (clojure.java.io/input-stream x) out)
    (.toByteArray out)))

(defn get-private-image
  "Returns an image found under `resources/private/images/`"
  [{:keys [path-params]}]
  (let [imagePath (:id path-params)]                      
    (response/ok 
     {:entries (slurp-bytes (io/file (str "resources/private/images/" imagePath)))
     #_ (io/file (str "private/images/" imagePath))})))

You’ll have to base64 encode the file content if you want to put it in json format.

ring.util.codec has a base64-encode function that can do the encoding server-side. However, that’s a very cumbersome way to serve an image.

The “/resources/private/images/myimage.jpg” is a lot more reasonable. You should be able to serve it like this:

(defn get-private-image
  "Returns an image found under `resources/private/images/`"
  [{:keys [path-params]}]
  (let [imagePath (:id path-params)]                      
    {:status 200
     :headers {"Content-Type" "image/jpeg"} ; Add some mimetype detection of course
     :body (io/file (str "resources/private/images/" imagePath))})))

If that gives you a JSON-encoding error on the server-side, then there’s probably something wrong with your middleware setup. If it’s a JSON error from the client-side, then that’s expected since you’re not serving JSON. The easiest way to “decode” the image is to just create an <img> tag pointing to it and let the browser do the work.