在Go中传递响应主体(response.Body)的有效方法是什么?

时间:2016-03-19 23:18:58

标签: image http go

如果我有一些代码,比如下面的示例,从链接中提取图像然后将其保存到磁盘,传递图像数据的最佳方法是什么?

我考虑过使用ioutil.ReadAll(res.Body)将其转换为[]byte但是传递它似乎很昂贵,尽管我无法从文档中看出它是否返回切片或数组。我还尝试返回指向res.Body *io.ReadCloser类型的指针,但我无法弄清楚如何正确调用指向接口上的.Close()方法。

我知道将保存代码移动到FetchImage函数可能是解决此问题的最简单方法,但我希望尽可能将这些部分分开。

type ImageData struct {
    Data      io.ReadCloser
    Name      string
}

func FetchImage(url string) (io.ReadCloser, error) {
    res, err := http.Get(url)
    if err != nil {
        return nil, err
    }
    return res.Body, nil
}

func Save(data *ImageData) error {
    defer data.Data.Close()
    file, err := os.Create(data.Name)
    defer file.Close()
    if err != nil {
        return err
    }
    _, err = io.Copy(file, data.Data)
    if err != nil {
        return err
    }
    return nil
}

func main() {
    body, err := fetcher.FetchImage("https://imgur.com/asdf.jpg")
    if err != nil {
        panic(err)
    }
    imageData := ImageData{body, "asdf.jpg"}
    saver := Saver{config.BaseDir, 1}
    err = saver.Save(&imageData)
    if err != nil {
        panic(err)
    }
}

此外,我是Go的新手,如果此代码中的任何内容看起来很糟糕,请告诉我。

2 个答案:

答案 0 :(得分:1)

使用ioutil.ReadAll。该函数返回一个字节片。

切片有效传递。切片是指向后备阵列的指针,长度和容量。

type ImageData struct {
    Data      []byte
    Name      string
}

func FetchImage(url string) ([]byte, error) {
    res, err := http.Get(url)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    if res.StatusCode != 200 {
        return nil, fmt.Errorf("%s: %d", url, res.StatusCode)
    }
    return ioutil.ReadAll(resp.Body)
}

func Save(data *ImageData) error {
    file, err := os.Create(data.Name)
    if err != nil {
        return err
    }
    defer file.Close()
    _, err := file.Write(data.Data)
    return err
}

您也可以传递响应正文,但请谨慎使用。必须关闭响应主体才能释放底层连接。问题中的代码确实关闭了响应主体,但是很难看到,因为响应主体在它关闭的函数下传递。

答案 1 :(得分:1)

在Go中,所有数组都有一个大小说明符,看起来像[8]byte。切片不会有这个,看起来像[]byte。内部切片只存储指向数据的指针,切片的长度以及切片的容量。这在64位系统上只有24个字节,因此您不必担心传递它。

但是,我建议您从*ImageData返回FetchImage,因为它已经包含了像图片名称这样的元数据。

至于为什么你不能指向一个界面,有一个帖子here on SO可以解释原因。

此外,在检查错误之前Save defer file.Close() .then(response => { this.parseJSONContent(response); }) 。你应该交换它,因为如果有错误,文件将是nil,并且可能会发生段错误。