2012-07-27 11 views
76

Gibt es eine Möglichkeit, eine große Datei mit Go herunterzuladen, die den Inhalt direkt in einer Datei speichert, anstatt sie im Speicher zu speichern, bevor sie in eine Datei geschrieben wird? Da die Datei so groß ist, wird der gesamte Speicher belegt, wenn sie im Speicher gespeichert wird, bevor sie in eine Datei geschrieben wird.Wie kann ich eine große Datei mit Go effizient herunterladen?

Antwort

167

Ich nehme an, Sie über http bedeuten herunterladen (Fehlerprüfungen der Kürze halber weggelassen):

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) 

den Körper des http.Response ein Reader ist, so dass Sie alle Funktionen nutzen zu können, die einen Reader nehmen, zu, z.B lesen Sie ein Stück nach dem anderen und nicht auf einmal. In diesem speziellen Fall erledigt io.Copy() die Gruntwork für Sie.

+55

Beachten Sie, dass "io.Copy" 32kb (maximal) aus der Eingabe liest und sie an die Ausgabe schreibt und dann wiederholt. Also mach dir keine Sorgen um die Erinnerung. –

-3
  1. Hier ist ein Beispiel. https://github.com/thbar/golang-playground/blob/master/download-files.go

  2. Auch ich gebe Ihnen einige Codes könnten Ihnen helfen.

Code:

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

Dieses Beispiel liest den gesamten Inhalt mit dem 'ioutil.ReadAll()' in den Speicher. Das ist in Ordnung, solange Sie mit kleinen Dateien zu tun haben. – eduncan911

+9

@ eduncan911, aber es ist nicht gut für diese Frage, die explizit über große Dateien spricht und nicht alles in den Speicher saugen will. –

+1

Genau richtig, deshalb habe ich das so kommentiert - damit andere das auch nicht für große Dateien verwenden können. – eduncan911

27

Eine beschreibende Version von Steve M Antwort.

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

Die Antwort ausgewählt oben io.Copy verwendet, ist genau das, was Sie brauchen, aber wenn Sie in zusätzlichen Funktionen wie die Wiederaufnahme von abgebrochenen Downloads, automatische Benennung von Dateien, Prüfsummenvalidierung oder die Überwachung der Fortschritte mehrerer Downloads interessiert sind, Check-out die grab Paket.

Verwandte Themen