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:
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)
}
}
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)
Now that we have a proper image, we need to send an HTTP response with it. I found two ways to do this:
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.This is quite straight forward:
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.")
}
}
Image
http.ResponseWriter
using the Write function.First we define a template that reads a raw image encoded in base 64:
var ImageTemplate string = `<!DOCTYPE html>Then we create a function that use this template:
<html lang="en"><head></head>
<body><img src="data:image/jpg;base64,{{.Image}}"></body>`
// writeImageWithTemplate encodes an image 'img' in jpeg format and writes it into ResponseWriter using a template.Notice that we are using the package encoding/base64 in order to transform the image in a raw image.
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.")
}
}
}
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.
<!DOCTYPE html>
<html lang="en"><head><title></title></head>
<body>
<img src="data:image/jpg;base64,/9j/2wCEAAgGBgc....KKAP/2Q==">
</body>
Here is the full example also available in this gist
Some links that helped me find this: