Search code examples
httpgo

Reading image from HTTP request's body in Go


I'm playing with Go (first time ever) and I want to build a tool to retrieve images from Internet and cut them (even resize) but I'm stuck on the first step.

package main

import (
  "fmt"
  "http"
)

var client = http.Client{}

func cutterHandler(res http.ResponseWriter, req *http.Request) {
  reqImg, err := client.Get("http://www.google.com/intl/en_com/images/srpr/logo3w.png")
  if err != nil {
    fmt.Fprintf(res, "Error %d", err)
    return
  }
  buffer := make([]byte, reqImg.ContentLength)
  reqImg.Body.Read(buffer)
  res.Header().Set("Content-Length", fmt.Sprint(reqImg.ContentLength)) /* value: 7007 */
  res.Header().Set("Content-Type", reqImg.Header.Get("Content-Type")) /* value: image/png */
  res.Write(buffer)
}

func main() {
  http.HandleFunc("/cut", cutterHandler)
  http.ListenAndServe(":8080", nil) /* TODO Configurable */
}

I'm able to request an image (let's use Google logo) and to get its kind and size.

Indeed, I'm just re-writing the image (look at this as a toy "proxy"), setting Content-Length and Content-Type and writing the byte slice back but I get it wrong somewhere. See how it looks the final image rendered on Chromium 12.0.742.112 (90304):

Grotesque result

Also I checked the downloaded file and it is a 7007 bytes PNG image. It should be working properly if we look at the request:

GET /cut HTTP/1.1
User-Agent: curl/7.22.0 (i486-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.0e zlib/1.2.3.4 libidn/1.23 libssh2/1.2.8 librtmp/2.3
Host: 127.0.0.1:8080
Accept: /

HTTP/1.1 200 OK
Content-Length: 7007
Content-Type: image/png
Date: Tue, 27 Dec 2011 19:51:53 GMT

[PNG data]

What do you think I'm doing wrong here?

Disclaimer: I'm scratching my own itch, so probably I'm using the wrong tool :) Anyway, I can implement it on Ruby but before I would like to give Go a try.

Update: still scratching itches but... I think this is going to be a good side-of-side project so I'm opening it https://github.com/imdario/go-lazor If it is not useful, at least somebody can find usefulness with the references used to develop it. They were for me.


Solution

  • I think you went too fast to the serve things part.

    Focus on the first step, downloading the image.

    Here you have a little program that downloads that image to memory.
    It works on my 2011-12-22 weekly version, for r60.3 you just need to gofix the imports.

    package main
    
    import (
        "log"
        "io"
        "net/http"
        "os"
    )
    
    const url = "http://www.google.com/intl/en_com/images/srpr/logo3w.png"
    
    func main() {
        // Just a simple GET request to the image URL
        // We get back a *Response, and an error
        res, err := http.Get(url)
    
        if err != nil {
            log.Fatalf("http.Get -> %v", err)
        }
    
        // We read all the bytes of the image
        // Types: data []byte
        data, err = io.ReadAll(res.Body)
    
        if err != nil {
            log.Fatalf("io.ReadAll -> %v", err)
        }
    
        // You have to manually close the body, check docs
        // This is required if you want to use things like
        // Keep-Alive and other HTTP sorcery.
        res.Body.Close()
    
        // You can now save it to disk or whatever...
        os.WriteFile("google_logo.png", data, 0666)
    
        log.Println("I saved your image buddy!")
    }
    

    Voilá!

    This will get the image to memory inside data.
    Once you have that, you can decode it, crop it and serve back to the browser.

    Hope this helps.