英文:
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)上呈现的最终图像的样子:
我还检查了下载的文件,它是一个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]
你认为我在这里做错了什么?
免责声明:我正在解决自己的问题,所以可能我使用了错误的工具 无论如何,我可以在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 (
"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:
<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 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 (
"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.
答案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 (
"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 */
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论