2012-07-27 1 views
76

Goを使用して大きなファイルをダウンロードする方法はありますか?コンテンツをファイルに書き込む前に、すべてをメモリに保存するのではなく、ファイルに直接保存しますか?ファイルが非常に大きいので、ファイルに書き込む前にすべてをメモリに格納すると、すべてのメモリが使い果たされます。Goを使用して大きなファイルを効率的にダウンロードするにはどうすればよいですか?

答えて

167

私はあなたが(エラーチェックが簡略化のために省略)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.Responseの体はリーダーである、あなたは、リーダーを取る任意の関数を使用することができますので、例えば一度にすべてではなく、一度にチャンクを読んでください。この具体的なケースでは、io.Copy()があなたのためにぞくぞっています。

+55

'io.Copy'は入力から32kb(最大)を読み込んで出力に書き込んだ後、繰り返すことに注意してください。だからメモリについて心配しないでください。 –

-3
  1. ここにサンプルがあります。 https://github.com/thbar/golang-playground/blob/master/download-files.go

  2. また、いくつかのコードが役立つかもしれません。

コード:スティーブ・Mの答えの

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) 
     } 
    } 
} 
+10

この例では、内容全体を 'ioutil.ReadAll()'でメモリに読み込みます。あなたが小さなファイルを扱っている限り、それは問題ありません。 – eduncan911

+9

@ eduncan911しかし、大きなファイルについて明示的に語り、それをすべてメモリに吸いたくないというこの質問に対しては問題ありません。 –

+1

これは私がそうコメントした理由です。大型ファイルにはこれを使わないことを他の人にも知ってもらうためです。 – eduncan911

27

より説明バージョン。

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 
} 
7

io.Copyを使用して、上記選択した答えはあなたが必要な正確に何ですが、あなたが壊れたダウンロードを再開などの追加機能に興味がある場合は、自動命名ファイル、チェックサム検証または複数のダウンロードの進捗状況を監視し、grabパッケージをチェックアウト。

関連する問題