Simplest way to display a picture in a webapp with Genie.jl

Hello, I’m an absolute beginner in Genie.jl and in web stack in general. I want to build a webpage which periodically generates a random picture using some julia code, and displays it.

To begin, I want to simply display a picture in the browser. The picture pic.png is my root folder. When I do

route("/") do 
  html("<img src=\"pic.png\"/>")
end

the picture does not appear in the browser. Could someone help me understand solving this basic task ?

Related question. Suppose that I have a Julia function generate which generates a picture in the form of an RGB array (or any other image format supported by Julia, or a Plots object, etc).
In my webapp, I basically want to call this function, collect the output x = generate(), save it in a file and display the result in an html page.
Is there a way to do this without saving the picture, but to directly embed pic into the html ?

I think the most immediate issue that you’re facing here is the path to image not being correct. I had to put my image into “public” directory for it to be successfully found, otherwise it throws a 404. Also, html() is not a recognized function, I have to use HTML(), all capital.

As for having a random image generated and rendered into HTML from memory, without writing to disk, I’m not sure… I managed to get a random image to generate every time I refresh the page with the following routes.jl code:

using Genie.Router, Images, ImageMagick
import Random.randstring

route("/") do
        newfilename = "$(randstring()).png"
        randimage = rand(RGB, 1000, 1000)
        ImageMagick.save("public/img/$newfilename", randimage)
        HTML("<img src='img/$newfilename' />")
end

A notable downside of this approach is that it generates and saves a random image into public/img, but doesn’t delete it thereafter. If I added code to delete the image at the end of the route() block, the image simply didn’t render when I visited the page. So definitely undesirable behavior if you plan for your web-app to be used a lot, for it to flood your disk with all sorts of data and never reclaim the used up space. If you find a better way, I would like to know about it!

1 Like

Hi, I know nothing of the Genie.jl part.
You can keep track of the created images and delete them when unused in this same method, a global counter can do the job, you can leave just the last few pictures in the folder…
NOTE: This just works if the program is persistent

using Genie.Router, Images, ImageMagick
import Random.randstring

img_counter = 1
img_pool_size = 5
img_name(c) = "rand_img_$c.png"

route("/") do
        # delete old images
        rm_path = joinpath("public", "img", img_name(counter - img_pool_size))
        rm(rm_path; force = true)

        # create new img
        newfilename = img_name(img_counter)
        randimage = rand(RGB, 1000, 1000)
        img_counter += 1
        save_path = joinpath("public", "img", newfilename)
        ImageMagick.save(save_path, randimage)

        # html part
        html_path = joinpath("img", newfilename)
        HTML("<img src='$html_path' />")
end
1 Like

You can base64 encode the image directly in the HTML. That way you do not need to implement additional image file server functionality or create temporary files.

image_stream = open(image_file) # This opens a file, but you can just as well use a in-memory buffer
data = Base64.base64encode(image_stream)
html = """<img src="data:image/png;base64,$(data)">"""
4 Likes

Very good to know. I wouldn’t have guessed that you can embed an image directly in HTML.

1 Like

Thank you all @stiven.roytman @josePereiro for your answers. It turned out that saving files in the public folder was sufficient.

@stiven.roytman when I use HTML(“hello”), here is the result :

while with html(“hello”) I simply get the normal behaviour “hello”.

@Krastanov Thanks, this looks really like what I was trying to do ! Unfortunately,

route("/other") do 
      rndpic = rand(RGB, 100, 100)
      data = Base64.base64encode(rndpic)
      i = """<img src="data:image/png;base64,$(data)">"""
      html("<img src='data:image/png;base64,$(data)'>")
end

results in nothing, the image does not show.

You have to encode the png stream. Instead, what you are doing is encoding some raw bitmap, which is not png, so the browser just sees some random data that can not be decoded. To do this properly, first create an in-memory file-like stream, “save” the image to that file-like object in a png format, then encode that file-like object into a base64 string.

2 Likes

@sim, the html thing was my bad, I just didn’t import Genie.Renderer.Html.

I think I figured out the base64 thing.

using Genie.Router, Genie.Renderer.Html 
using FileIO, Images, ImageMagick, Base64

route("/") do
        randimage = rand(RGB, 1000, 1000)
        buffer = Base.IOBuffer()
        ImageMagick.save(Stream(format"PNG", buffer), randimage)
        data = base64encode(take!(buffer))
        close(buffer)
        html("""<img src="data:image/png;base64,$(data)">""")
end

Thank you @Krastanov, super useful, there’s no way I would’ve solved this on my own.

2 Likes

Thanks everyone, worked like a charm.