在Go中从HTTP请求的主体中读取图像

huangapple go评论79阅读模式
英文:

Reading image from HTTP request's body in Go

问题

我正在使用Go(第一次)玩耍,并且我想构建一个从互联网上检索图像并对其进行裁剪(甚至调整大小)的工具,但我在第一步上卡住了。

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 */
}

我能够请求图像(让我们使用Google标志)并获取其类型和大小。

实际上,我只是重新编写图像(将其视为玩具“代理”),设置Content-Length和Content-Type,并将字节切片写回,但我在某个地方弄错了。看看在Chromium 12.0.742.112(90304)上呈现的最终图像的样子:

在Go中从HTTP请求的主体中读取图像

我还检查了下载的文件,它是一个7007字节的PNG图像。如果我们看一下请求,它应该正常工作:

<pre>
> GET /cut HTTP/1.1<br/>
> 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<br/>
> Host: 127.0.0.1:8080<br/>
> Accept: /
>
> HTTP/1.1 200 OK<br/>
> Content-Length: 7007<br/>
> Content-Type: image/png<br/>
> Date: Tue, 27 Dec 2011 19:51:53 GMT
>
> [PNG data]

你认为我在这里做错了什么?

免责声明:我正在解决自己的问题,所以可能我使用了错误的工具 在Go中从HTTP请求的主体中读取图像 无论如何,我可以在Ruby上实现它,但在尝试之前,我想试试Go。

更新:仍然在解决问题,但是...我认为这将是一个很好的并行项目,所以我正在开放它https://github.com/imdario/go-lazor 如果它没有用处,至少有人可以找到用于开发它的参考资料。对我来说,它们很有用。

英文:

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 (
  &quot;fmt&quot;
  &quot;http&quot;
)

var client = http.Client{}

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

func main() {
  http.HandleFunc(&quot;/cut&quot;, cutterHandler)
  http.ListenAndServe(&quot;:8080&quot;, 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):

在Go中从HTTP请求的主体中读取图像

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:

<pre>
> GET /cut HTTP/1.1<br/>
> 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<br/>
> Host: 127.0.0.1:8080<br/>
> Accept: /
>
> HTTP/1.1 200 OK<br/>
> Content-Length: 7007<br/>
> Content-Type: image/png<br/>
> 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 在Go中从HTTP请求的主体中读取图像 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.

答案1

得分: 13

我认为你在serve things part部分做得太快了。

专注于第一步,下载图像

这里有一个小程序,它将该图像下载到内存中。
它适用于我的2011-12-22周版本,对于r60.3,你只需要修复导入。

package main

import (
    "log"
    "io/ioutil"
    "net/http"
)

const url = "http://www.google.com/intl/en_com/images/srpr/logo3w.png"

func main() {
    // 只是一个简单的GET请求到图像URL
    // 我们得到一个*Response和一个错误
    res, err := http.Get(url)

    if err != nil {
        log.Fatalf("http.Get -> %v", err)
    }

    // 我们读取图像的所有字节
    // 类型:data []byte
    data, err = ioutil.ReadAll(res.Body)

    if err != nil {
        log.Fatalf("ioutil.ReadAll -> %v", err)
    }

    // 你必须手动关闭body,查看文档
    // 如果你想使用Keep-Alive和其他HTTP魔法,这是必需的。
    res.Body.Close()

    // 现在你可以将它保存到磁盘或其他地方...
    ioutil.WriteFile("google_logo.png", data, 0666)

    log.Println("我保存了你的图像伙计!")
}

Voilá!

这将把图像保存在data内存中。
一旦你有了它,你可以解码它,裁剪它并返回给浏览器。

希望对你有所帮助。

英文:

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 (
    &quot;log&quot;
    &quot;io/ioutil&quot;
    &quot;net/http&quot;
)

const url = &quot;http://www.google.com/intl/en_com/images/srpr/logo3w.png&quot;

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(&quot;http.Get -&gt; %v&quot;, err)
    }

    // We read all the bytes of the image
    // Types: data []byte
    data, err = ioutil.ReadAll(res.Body)

    if err != nil {
        log.Fatalf(&quot;ioutil.ReadAll -&gt; %v&quot;, 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(&quot;google_logo.png&quot;, data, 0666)

    log.Println(&quot;I saved your image buddy!&quot;)
}

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.

答案2

得分: 9

我尝试了你的代码,并注意到你提供的图像大小是正确的,但是文件在某个点之后的内容都是0x00。

请查阅io.Reader文档。重要的是要记住,Read方法读取最多你请求的字节数。它可以读取较少的字节而不返回错误。(你应该检查错误,但这里不是问题。)

如果你想确保缓冲区完全填满,可以使用io.ReadFull。在这种情况下,使用io.Copy将整个Reader的内容复制到缓冲区更简单。

还要记得关闭HTTP请求的主体。

我会这样重写代码:

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 {
		// 处理错误
	}
	reqImg.Body.Close()
}

func main() {
	http.HandleFunc("/cut", cutterHandler)
	http.ListenAndServe(":8080", nil) /* TODO 可配置 */
}
英文:

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 (
	&quot;fmt&quot;
	&quot;http&quot;
	&quot;io&quot;
)

var client = http.Client{}

func cutterHandler(res http.ResponseWriter, req *http.Request) {
	reqImg, err := client.Get(&quot;http://www.google.com/intl/en_com/images/srpr/logo3w.png&quot;)
	if err != nil {
		fmt.Fprintf(res, &quot;Error %d&quot;, err)
		return
	}
	res.Header().Set(&quot;Content-Length&quot;, fmt.Sprint(reqImg.ContentLength))
	res.Header().Set(&quot;Content-Type&quot;, reqImg.Header.Get(&quot;Content-Type&quot;))
	if _, err = io.Copy(res, reqImg.Body); err != nil {
		// handle error
	}
	reqImg.Body.Close()
}

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

huangapple
  • 本文由 发表于 2011年12月28日 03:54:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/8648682.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定