我想将数据从http响应复制到文件和缓冲区。
但是,我无法理解这一点。
最初我有这个:
func DownloadS3(hash string, cluster Cluster, tok *oauth.Token, offset, length int64, f *os.File) ([]byte, error) {
// ... other code not shown ...
resp, err = DoHTTPRequest("GET", s3tok.URL, nil, reqhdr, defaultClientTimeout)
if err != nil {
fmt.Println("Error downloading from cluster:", err)
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 && resp.StatusCode != 206 {
return nil, err
}
// Create buffer to return actual content
buf := make([]byte, resp.ContentLength)
// This actually does copy the whole downloaded data to the file as
// expected. However, I didn't expect io.CopyBuffer to not use the entire
// buffer. It ends up returning a portion of the file.
_, err = io.CopyBuffer(f, resp.Body, buf)
return buf, err
}
所以我想要的实际上就像是
_, err = io.CopyBuffer(f, io.TeeReader(resp.Body, buf), nil)
只是,我不能将buf传递给TeeReader,因为它没有实现编写器接口。我确定有一个合适的方法,但我无法找到它,因为我摸索 在寻找一种有效的方法来做到这一点。
如何在不在缓冲区之后分配缓冲区的情况下执行此操作。我试图提高效率。即写文件并将其读回来似乎很愚蠢。
我尝试过的但是没有按预期工作。
// Create buffer to return actual content
buf := make([]byte, resp.ContentLength)
_, err = io.CopyBuffer(f, io.TeeReader(resp.Body,bytes.NewBuffer(buf)), nil)
return buf, nil
答案 0 :(得分:2)
在数据写入文件时,使用io.MultiWriter和bytes.Buffer获取数据副本:
var buf bytes.Buffer
_, err := io.Copy(io.MultiWriter(f, &buf), resp.Body)
return buf.Bytes(), err
无法保证io.CopyBuffer会写入整个缓冲区。当在io.Reader.Read的单个调用中读取整个响应主体时,当前实现将仅使用整个缓冲区。在您的示例中,整个响应主体需要多次读取才能进行啜食。
答案 1 :(得分:1)
只是为了让(其他)答案清晰完整:io.TeeReader()
没有错误,你无法将整个正文复制到文件并将其作为bytes.Buffer
,但它是完全io.CopyBuffer()
的错。
io.Copy()
是骑士和闪亮的盔甲,持续复制直到整个输入被消耗:
将副本从src复制到dst,直到在src上达到EOF或发生错误。
使用io.TeeReader()
:
buf := bytes.NewBuffer(make([]byte, 0, resp.ContentLength))
_, err = io.Copy(f, io.TeeReader(resp.Body, buf))
return buf.Bytes(), err
像这样使用bytes.NewBuffer()
,你可以预先分配必要的缓冲区,避免重新分配和复制(这会使它更快)。当然,您也可以将其与io.MultiWriter()
一起使用。