2012-07-27 19 views
76

¿Hay alguna manera de descargar un archivo grande usando Go que almacenará el contenido directamente en un archivo en lugar de almacenarlo todo en la memoria antes de escribirlo en un archivo? Debido a que el archivo es tan grande, almacenarlo todo en la memoria antes de escribirlo en un archivo va a agotar toda la memoria.¿Cómo puedo descargar eficientemente un archivo grande usando Go?

Respuesta

167

Voy a asumir que decir descargar a través de http (comprobaciones de errores omitidos por razones de brevedad):

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) 

Administración de la http.Response es un lector, lo que puede utilizar cualquiera de las funciones que se llevan a un lector, que, p.ej lee un trozo a la vez en lugar de todos a la vez. En este caso específico, io.Copy() hace el gruntwork por usted.

+55

Tenga en cuenta que 'io.Copy' lee 32kb (máximo) desde la entrada y los escribe en la salida, luego se repite. Así que no te preocupes por la memoria. –

-3
  1. Aquí hay un ejemplo. https://github.com/thbar/golang-playground/blob/master/download-files.go

  2. También te doy algunos códigos que pueden ayudarte.

código:

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

Este ejemplo lee todo el contenido en la memoria, con 'ioutil.ReadAll()'. Está bien, siempre y cuando se trate de archivos pequeños. – eduncan911

+9

@ eduncan911, pero no está bien para esta pregunta que explícitamente habla de archivos grandes y no quiere absorberlo todo en la memoria. –

+1

Exactamente, por eso lo comenté, para que otros también lo sepan y no lo usen para archivos de gran tamaño. – eduncan911

27

Una versión más descriptivo de la respuesta de 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 
} 
7

La respuesta seleccionada anteriormente utilizando io.Copy es exactamente lo que necesita, pero si usted está interesado en las características adicionales como la reanudación de descargas interrumpidas, archivos de denominación automática, validación de la suma o monitorear el progreso de las descargas múltiples, pago y envío del paquete grab.

Cuestiones relacionadas