如何使用Go高效下载大文件?

如何使用Go高效下载大文件?,go,Go,有没有一种方法可以使用Go下载一个大文件,将内容直接存储到文件中,而不是在写入文件之前将其全部存储在内存中?由于文件太大,在将其写入文件之前将其全部存储在内存中会耗尽所有内存。我假定您的意思是通过http下载(为了简洁起见,省略了错误检查): 响应的主体是一个读卡器,因此您可以使用任何带读卡器的函数,例如,一次读取一个块,而不是一次读取所有块。在这种特定情况下,io.Copy()为您完成繁重的工作 这是一个样本 此外,我给你一些代码可能会帮助你 代码: Steve M回答的更具描述性的版本 im

有没有一种方法可以使用Go下载一个大文件,将内容直接存储到文件中,而不是在写入文件之前将其全部存储在内存中?由于文件太大,在将其写入文件之前将其全部存储在内存中会耗尽所有内存。

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

响应的主体是一个读卡器,因此您可以使用任何带读卡器的函数,例如,一次读取一个块,而不是一次读取所有块。在这种特定情况下,
io.Copy()
为您完成繁重的工作

  • 这是一个样本

  • 此外,我给你一些代码可能会帮助你

  • 代码:


    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
    选择的答案正是您所需要的,但如果您对恢复中断下载、自动命名文件、校验和验证或监视多个下载的进度等其他功能感兴趣,请签出软件包。

    注意
    io.Copy
    的最大读取容量为32kb从输入并将其写入输出,然后重复。因此,不要担心内存问题。如何取消下载进度?您可以使用此选项在给定超时后取消下载
    client:=http.client{timeout:10*time.Second,}client.Get(“http://example.com/“”
    此示例使用
    ioutil.ReadAll()
    将整个内容读取到内存中。这很好,只要你处理的是小文件。@eduncan911,但对于这个明确谈论大文件并且不想将其全部存储在内存中的问题来说,这并不好。完全正确,这就是我为什么这么评论的原因-让其他人也知道,不要将其用于大文件。这不是一个好的答案,实际上应该被移除。在一大堆代码中使用ReadAll是一个潜在的问题,需要等待一个大文件被使用。发生的情况是,如果大文件上有ReadAll,通常的反应是伴随着高内存消耗和AWS账单的增加,直到出现故障。当问题被发现时,账单已经很高了。你能添加一个代码片段以确保在链接被弃用时信息不会丢失吗?在我的世界里,我实现了一个DSL,需要下载一个文件。。。Exec()curl很方便,直到我遇到了一些操作系统兼容性和chroot问题,我真的不想配置这些问题,因为它是一个合理的安全模型。所以你用这段代码替换了我的CURL,并得到了10-15倍的性能提升。哼!
    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)
            }
        }
    }
    
    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
    }