मैं गो का उपयोग करके एक बड़ी फ़ाइल को कुशलता से कैसे डाउनलोड कर सकता हूं?


106

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

जवाबों:


214

मैं मान लूंगा कि आप 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's बॉडी एक रीडर है, इसलिए आप किसी भी फ़ंक्शंस का उपयोग कर सकते हैं जो रीडर को, जैसे, एक बार में एक बार में एक बार पढ़ने के बजाय एक बार एक चंक पढ़ता है। इस विशिष्ट मामले में, io.Copy()आपके लिए ग्रन्टवर्क करता है।


85
ध्यान दें कि io.Copyइनपुट से 32kb (अधिकतम) पढ़ता है और उन्हें आउटपुट पर लिखता है, फिर दोहराता है। तो स्मृति के बारे में चिंता मत करो।
मोशे रेवह

डाउनलोड प्रगति कैसे रद्द करें?
जेलन यांग

आप इसे दिए गए समय समाप्त होने के बाद डाउनलोड को रद्द करने के लिए उपयोग कर सकते हैंclient := http.Client{Timeout: 10 * time.Second,} client.Get("http://example.com/")
भरत कुमार

55

स्टीव एम के जवाब का अधिक वर्णनात्मक संस्करण।

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
}

1
मेरे ब्रह्मांड में मैंने एक डीएसएल लागू किया है जो एक फ़ाइल को डाउनलोड करने की आवश्यकता है ... यह Exec () कर्ल के लिए सुविधाजनक था जब तक कि मैं कुछ ओएस कॉम्पिटिटर और चेरोट मुद्दों में गिर गया था कि मैं वास्तव में चारों ओर कॉन्फ़िगर नहीं करना चाहता था क्योंकि यह एक समझदार सुरक्षा मॉडल है। इसलिए यू ने मेरे CURL को इस कोड से बदल दिया और 10-15x प्रदर्शन में सुधार हुआ। ओह!
रिचर्ड

14

उपयोग करके ऊपर चुना गया उत्तर io.Copy का वही अर्थ है जिसकी आपको आवश्यकता है, लेकिन यदि आप टूटी हुई डाउनलोडिंग, ऑटो-नामकरण फ़ाइलें, चेकसम सत्यापन या कई डाउनलोड की प्रगति की निगरानी जैसी अतिरिक्त सुविधाओं में रुचि रखते हैं, तो पकड़ो पैकेज चेकआउट करें ।


क्या आप यह सुनिश्चित करने के लिए एक कोड स्निपेट जोड़ सकते हैं कि यदि लिंक अस्वीकृत हो जाता है तो जानकारी खो नहीं जाएगी?
०३०

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

13
यह उदाहरण संपूर्ण सामग्री को मेमोरी में पढ़ता है, के साथ ioutil.ReadAll()। यह ठीक है, जब तक आप छोटी फ़ाइलों के साथ काम कर रहे हैं।
eduncan911

13
@ eduncan911, लेकिन यह इस सवाल के लिए ठीक नहीं है जो स्पष्ट रूप से बड़ी फ़ाइलों के बारे में बात करता है और यह सब स्मृति में चूसना नहीं चाहता है।
डेव सी

2
बिल्कुल सही, यही कारण है कि मैंने ऐसा टिप्पणी की - दूसरों को जानने के लिए और बड़ी फ़ाइलों के लिए इसका उपयोग नहीं करने के लिए।
eduncan911

4
यह एक सौम्य जवाब नहीं है, और वास्तव में हटाया जाना चाहिए। कोड के एक बड़े ढेर के बीच ReadAll का उपयोग एक अव्यक्त समस्या है जब तक कि बड़ी फ़ाइल का उपयोग नहीं किया जाता है। क्या होता है कि अगर बड़ी फ़ाइलों पर रीडअॉल होते हैं, तो आमतौर पर प्रतिक्रिया उच्च मेमोरी खपत के साथ जाने के लिए होती है और कुछ विफल होने तक एडब्ल्यूएस बिल में वृद्धि होती है। जब तक समस्या का पता चलता है, तब तक बिल पहले से ही अधिक होते हैं।
रोब
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.