如何使用 Go 有效地下载大型文件?

有没有一种方法可以使用 Go 下载一个大型文件,将内容直接存储到一个文件中,而不是在将其写入文件之前将其全部存储在内存中?因为文件非常大,在写入文件之前将它们全部存储在内存中将耗尽所有内存。

95736 次浏览

我假设你的意思是通过 http 下载(为简洁起见省略了错误检查) :

import ("net/http"; "io"; "os")
...
out, err := os.Create("output.txt")
defer out.Close()
...
resp, err := http.Get("http://example.com/")
defer resp.Body.Close()
...
n, err := io.Copy(out, resp.Body)

Http://http://http://http://http://http://.Response 的 Body 是一个 Reader,因此您可以使用任何带有 Reader 的函数,例如,一次读取一个块,而不是一次全部读取。在这个特定的例子中,io.Copy()为您完成了基础工作。

  1. 这是一个样本。 https://github.com/thbar/golang-playground/blob/master/download-files.go

  2. 还有,我给你一些密码也许能帮到你。

密码:

func HTTPDownload(uri string) ([]byte, error) {
fmt.Printf("HTTPDownload From: %s.\n", uri)
res, err := http.Get(uri)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
d, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("ReadFile: Size of download: %d\n", len(d))
return d, err
}


func WriteFile(dst string, d []byte) error {
fmt.Printf("WriteFile: Size of download: %d\n", len(d))
err := ioutil.WriteFile(dst, d, 0444)
if err != nil {
log.Fatal(err)
}
return err
}


func DownloadToFile(uri string, dst string) {
fmt.Printf("DownloadToFile From: %s.\n", uri)
if d, err := HTTPDownload(uri); err == nil {
fmt.Printf("downloaded %s.\n", uri)
if WriteFile(dst, d) == nil {
fmt.Printf("saved %s as %s\n", uri, dst)
}
}
}

Steve M 的回答的更具描述性的版本。

import (
"os"
"net/http"
"io"
)


func downloadFile(filepath string, url string) (err error) {


// Create the file
out, err := os.Create(filepath)
if err != nil  {
return err
}
defer out.Close()


// Get the data
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()


// Check server response
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("bad status: %s", resp.Status)
}


// Writer the body to file
_, err = io.Copy(out, resp.Body)
if err != nil  {
return err
}


return nil
}

上面使用 io.Copy选择的答案正是您所需要的,但是如果您对其他特性感兴趣,比如恢复中断的下载、自动命名文件、校验和验证或监视多个下载的进度,请检查 抓住包。