如何在Go中将HTTP响应导入文件?

56

如何将下面的代码转换为使用流/管道,以便无需将全部内容读入内存? 类似于: http.Get("http://example.com/").Pipe("./data.txt")

package main
import ("net/http";"io/ioutil")

func main() {
        resp, err := http.Get("http://example.com/")
        check(err)
        defer resp.Body.Close()
        body, err := ioutil.ReadAll(resp.Body)
        check(err)
        err = ioutil.WriteFile("./data.txt", body, 0666)
        check(err)
}
func check(e error) {
        if e != nil {
                panic(e)
        }
}

1
Get的响应包含一个实现Reader接口的Body字段。你基本上可以将其传递给某种函数,将其内容写入文件中。除非你请求,否则不会将整个body的内容加载到内存中。 - fuz
更重要的是检查resp.StatusCode是否为200。 - Amnon
2个回答

81

那么 io.Copy() 怎么样?它的文档可以在此处找到:http://golang.org/pkg/io/#Copy

不过它相当简单。给它一个 io.Reader 和一个 io.Writer,它就会逐个小块地拷贝数据(例如并非一次性全部在内存中操作)。

因此,您可以尝试编写类���以下的代码:

func main() {
  resp, err := http.Get("...")
  check(err)
  defer resp.Body.Close()
  out, err := os.Create("filename.ext")
  if err != nil {
    // panic?
  }
  defer out.Close()
  io.Copy(out, resp.Body)
}

我没有测试过上述内容;我只是从你上面的例子中快速地把它拼凑在一起,但如果不是百分之百正确,也应该很接近。


4
请注意,此代码未检查Close()语句的任何错误返回。 - Nick Craig-Wood
6
@Nick,这非常普遍(甚至成为惯用语),推迟关闭并忽略错误。对于关闭错误,你通常无法做太多事情,除了对操作系统大喊大叫。 - mna
10
这里有一个名为checkClose的示例,展示了如何在延迟关闭时检查错误。如果您注重数据完整性,请不要忽略关闭操作的错误。如果关闭返回一个错误,您需要向用户报告。来自Linux手册页的说明:不检查close()函数的返回值是一种常见但非常严重的编程错误。之前write()操作可能出现的错误很可能在最终的close()函数中首次报告。在关闭文件时不检查返回值可能导致数据默默丢失。 - Nick Craig-Wood
10
您可以使用它来延迟checkClose(io, &err)函数的执行,这将更改返回函数的返回代码以指示在关闭操作中发生了错误,提供了延迟但正确返回错误的便利性 - Nick Craig-Wood
15
我很惊讶有很多评论关于在 Close() 中没有检查错误的问题(这是个好主意!),但没有人提到(可能更为重要的)未检查 io.Copy 的错误返回值。 - Dave C
显示剩余6条评论

8

另一个选项是 File.ReadFrom:

package main

import (
   "net/http"
   "os"
)

func main() {
   r, e := http.Get("http://speedtest.lax.hivelocity.net")
   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)
}

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