2012-07-27 13 views
76

क्या गो का उपयोग करके एक बड़ी फ़ाइल डाउनलोड करने का कोई तरीका है जो सामग्री को फ़ाइल में लिखने से पहले इसे स्मृति में संग्रहीत करने के बजाय सीधे फ़ाइल में संग्रहीत करेगा? चूंकि फ़ाइल इतनी बड़ी है, इसे फाइल में लिखने से पहले इसे स्मृति में संग्रहीत करना सभी मेमोरी का उपयोग करने जा रहा है।मैं गो का उपयोग कर एक बड़ी फाइल को कुशलतापूर्वक कैसे डाउनलोड कर सकता हूं?

उत्तर

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. मैं आपको कुछ कोड भी आपकी मदद कर सकता हूं।

कोड:

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 पैकेज चेकआउट ठीक है।

संबंधित मुद्दे