Display image from database, best way

I wanted to know the best practice to display (small) images (like thumbnails) saved in the database. I want to display in a Liveview.

I can serve them by assigning to the src attribute some data url: "data:image/*;base64, "<> Base.encode64( -- binary_from_db --)".

Alternatively, you can serve these images as assets but they have to be temporary and I don’t really see how I can do this. I had a look at the Plug.Upload. Is this useful in this situation? You create a temporary file in the file system, write data in it, but you can’t serve them as the path is “/var/folder/…” , unless I have to serve this static folder?

pu = %Plug.Upload{
  path: Plug.Upload.random_file!("test"),
  filename: name_from_db,
  content_type: "image/*"
}
File.write(pu.path, binary_from_db)

I’d suggest a plain old controller, which pulls the contents out of the db and serves it with the necessary headers. If you have a process alive for the whole lifecycle of those images you could consider something like Plug.Upload, where you maintain and cleanup images on the filesystem.

1 Like

On the LV mount, if there is a user, I load his data, including these images/thumbnails. I am confused, I don’t have any controller involved.

Browsers don’t have (reasonable) means of loading files via websocket connections. My suggestions is to not load that stuff in the LV, but link those assets using a route, which is served by a controller, which handles the asset handling.

4 Likes

Agreed. You can do mildly hacky things like base64 encode the image data and shove it over the wire to a hook but it’d be an overcomplicated answer compared to a good old fashioned assets controller.

1 Like

Yes indeed.

So it could be something like <img src={~p"/images/#{@id}"}/> in the LV, and the endpoint “/images” would be served by a controller.

In the controller, retrieve the image from the db, load it into a Plug.Uploader, maybe cross check the mime-type (eg the unix command “file”), and then send a response with the content-type:

conn
|> put_content_type(mime-type)
|> render(200, image)

and then hope that Plug.Upload will wipe out the temporary file.

This part isn’t even necessary. You literally just pull the image from the DB and respond with it, what do you need the uploader for?

Sending and displaying image as base64 through LiveView is definitely possible. After all browsers support displaying base64 in an img tag.

I’ve done it in the early days of LV from actual files, it faster to load them in Elixir, convert them to base64 and update the img tag through the websocket than just updating the img tag with the image url (it was http1 not http2, which may be faster with the connection kept open).

I needed an asset for PHoenix to serve the image, but this is without the controller version. I don’t know if I can just send the data from the db directly, If this is the case, then no PLug.Uploader.

I works, sure, but it just doesn’t feel right so I asked :slight_smile:

Well I would question more storing images in the database than sending them through a websocket once there are queried anyway :wink:

I think for thumbnails that’s suitable.

The downside of not having actual files is no browser caching.

Yes, I just wanted to evaluate serving the thumbnails (4kB) from the db vs from say S3.