简体   繁体   中英

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):

怪诞的结果

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.

I tried your code and noticed that the image you were serving was the right size, but the contents of the file past a certain point were all 0x00.

Review the io.Reader documentation . The important thing to remember is that Read reads up to the number of bytes you request. It can read fewer with no error returned. (You should be checking the error too, but that's not an issue here.)

If you want to make sure your buffer is completely full, use io.ReadFull. In this case it's simpler to just copy the entire contents of the Reader with io.Copy.

It's also important to remember to close HTTP request bodies.

I would rewrite the code this way:

package main

import (
    "fmt"
    "http"
    "io"
)

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
    }
    res.Header().Set("Content-Length", fmt.Sprint(reqImg.ContentLength))
    res.Header().Set("Content-Type", reqImg.Header.Get("Content-Type"))
    if _, err = io.Copy(res, reqImg.Body); err != nil {
        // handle error
    }
    reqImg.Body.Close()
}

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

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/ioutil"
    "net/http"
)

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 = ioutil.ReadAll(res.Body)

    if err != nil {
        log.Fatalf("ioutil.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...
    ioutil.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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM