在Go中将io.ReadCloser流式传输到文件的最有效方法是什么?

12

给定一个io.ReadCloser,例如来自HTTP请求的响应,最有效的方式是什么,既可以降低内存开销,又可以提高代码可读性,将响应流式传输到文件中?


2
你为什么不能使用io.Copy呢? - user3591723
一开始我实际上没有发现。在Go语言中,接口使得你不知道哪些类型实现了哪些接口。使用 Copy(dst Writer, src Reader) 时,不太清楚io.ReadCloser和File实现了Reader和Writer接口。 - mbrevoort
1
你应该了解一些Go语言的命名规范,这样就很清楚io.ReadCloser实现了Reader(和Closer)。我猜File需要一点挖掘,但人们会期望能够读写文件。 - user3591723
2个回答

21

io.Copy毫无疑问在代码方面是最高效的;您只需要

outFile, err := os.Create(filename)
// handle err
defer outFile.Close()
_, err = io.Copy(outFile, res.Body)
// handle err

从 CPU 和内存的角度来看,这也很可能非常高效。如果您想了解更多,可以查看io.Copy 的实现; 假设正文没有实现 WriteTo 并且文件没有实现 ReadFrom(快速浏览表明它们没有),Copy 将一次复制多达32kB的数据块。使用更大的块可能会使用更少的CPU,但会使用更多的内存; 他们选择的值似乎是一个很好的折衷。


2
说到缓冲区大小,go1.5将拥有一个io.CopyBuffer,它允许您传递要用于复制操作的缓冲区。 - JimB
@JimB 哦,非常好 :) - hobbs
1
在Go 1.15及更高版本中,答案中的io.Copy调用在幕后调用outfile.ReadFrom(res.Body) - Charlie Tumahai

0

另一个选项是File.ReadFrom

package main

import (
   "net/http"
   "os"
)

func main() {
   r, e := http.Get("https://stackoverflow.com")
   if e != nil {
      panic(e)
   }
   defer r.Body.Close()
   f, e := os.Create("index.html")
   if e != nil {
      panic(e)
   }
   defer f.Close()
   f.ReadFrom(r.Body)
}

https://golang.org/pkg/os#File.ReadFrom


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接