2011-12-27 111 views
1

我正在玩Go(有史以来第一次),我想构建一个工具来从Internet检索图像并剪切它们(甚至是调整大小),但是我被困在第一步。从HTTP请求的身体在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并将字节片段写回,但我在某处遇到了错误。看看它的外观呈现铬12.0.742.112(90304)最终图像:

Grotesque result

而且我检查下载的文件,这是一个7007个字节的PNG图片。

GET /切HTTP/1.1
的User-Agent:如果我们看一下请求应正常工作卷曲/ 7.22.0(1486-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
主机:127.0.0.1:8080
接受:/

HTTP/1.1 200 OK
内容长度:7007
内容类型:图像/ PNG
日期:星期二,2011年12月27日19时51分53秒GMT

[PNG数据]

你觉得我做错了什么?免责声明:我抓我自己的痒,所以可能我使用了错误的工具:)无论如何,我可以在Ruby上实现它,但在我想尝试一下之前。

更新:仍在抓痒,但...我认为这将是一个很好的并排项目,所以我打开它https://github.com/imdario/go-lazor如果它没有用,至少有人可以找到有用的参考用于开发它。他们是为我。

回答

8

我试了一下你的代码,发现你提供的图片大小合适,但是文件内容超过了某个点就是0x00。

查看io.Reader documentation。重要的是要记住的是,读取高达您请求的字节数。它可以读取更少且没有返回错误。 (您应该检查错误,但这不是问题。)

如果您想确保缓冲区已满,请使用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 { 
     // handle error 
    } 
    reqImg.Body.Close() 
} 

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

我将在稍后检查。你说的话看起来是正确的(我没有想到在图像中可能的0x0字节)。谢谢! – 2011-12-28 07:35:35

+0

io.Copy无法按预期工作。请求在写入标题后截断,我没有发现任何错误。 无论如何,我用ReadFull解决了问题:https://gist.github.com/1528886如果需要,请使用我所用的内容编辑解决方案,仅供参考。当你指出正确的方式时,我接受你的答案。谢谢! – 2011-12-28 17:56:01

+0

对不起,有一个错误,导致它不工作(混淆req和reqImg)。我修复了代码,它可以与io.Copy一起使用。 – 2011-12-29 21:16:56

7

我想你去得太快的服务的事情的一部分

专注于第一步,下载图像

这里有一个将该图像下载到内存的小程序。
它适用于我的2011-12-22每周版本,对于r60.3您只需要gofix进口。

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á!

这会将图像传到data内部的存储器中。
一旦你有了,你可以对它进行解码,裁剪并返回浏览器。

希望这会有所帮助。