I am currently building an avatar generator with rjourde in Go.
In the process of doing it I am learning about how to create images on the fly and display them in an html page.
Here is what I found today:

Objective

I want to have a simple web server, with two entry points: /blue and /red.
This two entry points will display a blue or red image that will be created on the fly.

    var root = flag.String("root", ".", "file system path")

func main() {
http.HandleFunc("/blue/", blueHandler)
http.HandleFunc("/red/", redHandler)
http.Handle("/", http.FileServer(http.Dir(*root)))
log.Println("Listening on 8080")
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenAndServe:", err)
}
}

Building an image:

For this specific example there is no much complexity in the image I want to create. So a simple RGBA image will do the work. We do this with three golang packages: image, image/color, image/draw.

    m := image.NewRGBA(image.Rect(0, 0, 240, 240))
blue := color.RGBA{0, 0, 255, 255}
draw.Draw(m, m.Bounds(), &image.Uniform{blue}, image.ZP, draw.Src)

Write image to http.ResponseWriter:

Now that we have a proper image, we need to send an HTTP response with it. I found two ways to do this:

Option 1: Send an HTTP response of type image/jpeg:
The first option is to write an HTTP response of type image/jpeg or any format you want. This is what WriteImage does:
    // writeImage encodes an image 'img' in jpeg format and writes it into ResponseWriter.
func writeImage(w http.ResponseWriter, img *image.Image) {

buffer := new(bytes.Buffer)
if err := jpeg.Encode(buffer, *img, nil); err != nil {
log.Println("unable to encode image.")
}

w.Header().Set("Content-Type", "image/jpeg")
w.Header().Set("Content-Length", strconv.Itoa(len(buffer.Bytes())))
if _, err := w.Write(buffer.Bytes()); err != nil {
log.Println("unable to write image.")
}
}
This is quite straight forward:
  • Get an Image
  • Encode it with a specific format.
  • Write the array of bytes to the http.ResponseWriter using the Write function.

Option 2: Use a http/template to send the image:

First we define a template that reads a raw image encoded in base 64:

    var ImageTemplate string = `<!DOCTYPE html>
<html lang="en"><head></head>
<body><img src="data:image/jpg;base64,{{.Image}}"></body>`
Then we create a function that use this template:
    // writeImageWithTemplate encodes an image 'img' in jpeg format and writes it into ResponseWriter using a template.
func writeImageWithTemplate(w http.ResponseWriter, img *image.Image) {

buffer := new(bytes.Buffer)
if err := jpeg.Encode(buffer, *img, nil); err != nil {
log.Println("unable to encode image.")
}

str := base64.StdEncoding.EncodeToString(buffer.Bytes())
if tmpl, err := template.New("image").Parse(ImageTemplate); err != nil {
log.Println("unable to parse image template.")
} else {
data := map[string]interface{}{"Image": str}
if err = tmpl.Execute(w, data); err != nil {
log.Println("unable to execute template.")
}
}
}
Notice that we are using the package encoding/base64 in order to transform the image in a raw image.

str := base64.StdEncoding.EncodeToString(buffer.Bytes())
This is important, if you don't do this and only pass an array of bytes the browser will not be able to generate the image as it expects an image encoded in base64.

If you look at the generated HTML source code you would see something like this:

<!DOCTYPE html>
<html lang="en"><head><title></title></head>
<body>
<img src="data:image/jpg;base64,/9j/2wCEAAgGBgc....KKAP/2Q==">
</body>

All together:

Here is the full example also available in this gist



Some links that helped me find this:

Santiaago