मैं HTTP पर एक बाइनरी फ़ाइल कैसे डाउनलोड करूं?


131

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

URL है http://somedomain.net/flv/sample/sample.flv

मैं विंडोज प्लेटफॉर्म पर हूं और मैं किसी भी बाहरी कार्यक्रम को नहीं चलाना पसंद करूंगा।


मेरा समाधान दृढ़ता से snippets.dzone.com/posts/show/2469 पर आधारित है, जो फायरफॉक्स एड्रेस बार में रूबी फ़ाइल डाउनलोड टाइप करने के बाद दिखाई दिया ... तो क्या आपने यह प्रश्न पूछने से पहले इंटरनेट पर कोई शोध किया था?
दाविद

@ डेज्यू: मैंने शोध किया और यहां एक उत्तर दिया गया प्रश्न पाया गया। मूल रूप से उसी कोड के साथ जो आपने मुझे दिया था। resp.bodyभाग मुझे भ्रामक मैंने सोचा कि यह केवल 'शरीर की प्रतिक्रिया का हिस्सा बचत होगी, लेकिन मैं पूरी / बाइनरी फ़ाइल सहेजना चाहते हैं। मैंने यह भी पाया कि rio.rubyforge.org मददगार हो सकता है। इसके अलावा मेरे सवाल के साथ कोई भी यह नहीं कह सकता है कि इस तरह के सवाल का जवाब अभी तक नहीं दिया गया है
Radek

3
शरीर का हिस्सा बिल्कुल पूरी फ़ाइल है। प्रतिक्रिया हेडर (http) और बॉडी (फ़ाइल) से बनाई गई है, इसलिए जब आप शरीर को सहेजते हैं तो आपने फ़ाइल को सहेजा है ;-)
Dawid

1
एक और सवाल ... मान लीजिए कि फ़ाइल 100MB बड़ी है और डाउनलोड प्रक्रिया बीच में ही बाधित हो जाती है। वहाँ कुछ भी बचाया जा रहा है? क्या मैं फ़ाइल को फिर से शुरू कर सकता हूं?
राडेक

दुर्भाग्य से नहीं, क्योंकि http.get('...')कॉल एक अनुरोध भेजता है और प्रतिक्रिया (संपूर्ण फ़ाइल) प्राप्त करता है। किसी फ़ाइल को चंक्स में डाउनलोड करने के लिए और उसे एक साथ सहेजने के लिए नीचे दिए गए मेरे संपादित उत्तर को देखें;; पुनः शुरू करना आसान नहीं है, हो सकता है कि आप बाइट्स की गिनती करें और आपने फ़ाइल को फिर से डाउनलोड करने पर उन्हें छोड़ दिया हो और जब आप file.write(resp.body)बाइट्स की संख्या लौटाते हैं तो उन्हें छोड़ दें ।
दाविद

जवाबों:


143

सबसे सरल तरीका प्लेटफ़ॉर्म-विशिष्ट समाधान है:

 #!/usr/bin/env ruby
`wget http://somedomain.net/flv/sample/sample.flv`

संभवतः आप इसे खोज रहे हैं:

require 'net/http'
# Must be somedomain.net instead of somedomain.net/, otherwise, it will throw exception.
Net::HTTP.start("somedomain.net") do |http|
    resp = http.get("/flv/sample/sample.flv")
    open("sample.flv", "wb") do |file|
        file.write(resp.body)
    end
end
puts "Done."

संपादित करें: परिवर्तित धन्यवाद।

Edit2: समाधान जो डाउनलोड करते समय किसी फ़ाइल का हिस्सा बचाता है:

# instead of http.get
f = open('sample.flv')
begin
    http.request_get('/sample.flv') do |resp|
        resp.read_body do |segment|
            f.write(segment)
        end
    end
ensure
    f.close()
end

15
हाँ मैं जानता हूँ। इसलिए मैंने कहा कि यह है a platform-specific solution
दाविद

1
अधिक प्लेटफ़ॉर्म-विशिष्ट समाधान: GNU / Linux प्लेटफ़ॉर्म प्रदान करते हैं wget। OS X प्रदान करता है curl( curl http://oh.no/its/pbjellytime.flv --output secretlylove.flv)। Windows में Powershell समकक्ष है (new-object System.Net.WebClient).DownloadFile('http://oh.no/its/pbjellytime.flv','C:\tmp\secretlylove.flv')। डाउनलोड के माध्यम से सभी ऑपरेटिंग सिस्टम के लिए बायनेरिज़ विथ व कर्ल मौजूद हैं। मैं अभी भी अत्यधिक मानक पुस्तकालय का उपयोग करने की सलाह देता हूं जब तक कि आपका लेखन कोड केवल आपके अपने प्रेमी के लिए नहीं है। '
fny

1
शुरुआत ... सुनिश्चित करें कि ओपन ब्लॉक फॉर्म का उपयोग किया जाए तो यह आवश्यक नहीं है। open 'sample.flv' करना | f | .... f.write खंड
lab419

1
गैर-पाठ फ़ाइल दूषित हो जाती है।
पॉल

1
मैं का उपयोग कर chunked डाउनलोड का उपयोग करें Net::HTTP। और मुझे फ़ाइल का हिस्सा मिलता है, लेकिन प्रतिक्रिया मिलती है Net::HTTPOK। क्या यह सुनिश्चित करने का कोई तरीका है कि हम फ़ाइल को पूरी तरह से डाउनलोड करें?
निकोले कोंडरांटेंको

118

मुझे पता है कि यह एक पुराना प्रश्न है, लेकिन Google ने मुझे यहां फेंक दिया और मुझे लगता है कि मुझे इसका सरल उत्तर मिला।

में Railscasts # 179 , रयान बेट्स रूबी मानक वर्ग इस्तेमाल किया OpenURI ज्यादा क्या इस तरह कहा गया था की क्या करना है:

( चेतावनी : अनुपयोगी कोड। आपको इसे बदलने / मोड़ने की आवश्यकता हो सकती है।)

require 'open-uri'

File.open("/my/local/path/sample.flv", "wb") do |saved_file|
  # the following "open" is provided by open-uri
  open("http://somedomain.net/flv/sample/sample.flv", "rb") do |read_file|
    saved_file.write(read_file.read)
  end
end

9
open("http://somedomain.net/flv/sample/sample.flv", 'rb')बाइनरी मोड में URL खोलेंगे।
झोली

1
किसी को पता है कि क्या ओपन-यूआई बफर भरने के बारे में बुद्धिमान है जैसा कि @ इसा ने बताया?
21

1
यदि आप उसके लिए कोई नया प्रश्न खोलते हैं तो @gildefino आपको अधिक उत्तर देगा। यह संभावना नहीं है कि बहुत से लोग इसे पढ़ेंगे (और स्टैक ओवरफ्लो में ऐसा करना भी उचित है)।
किकिटो

2
बहुत बढ़िया। मुझे HTTP=> HTTPSपुनर्निर्देशन की समस्या थी , और यह पता चला कि open_uri_redirectionsजेम
मैथिलियो

1
एफडब्ल्यूआईडब्ल्यू कुछ लोगों को लगता है कि ओपन-यूआरआई खतरनाक है क्योंकि यह लाइब्रेरी कोड सहित सभी कोड को बंद कर देता है, जो openएक नई क्षमता के साथ उपयोग करता है जो कॉलिंग कोड का अनुमान नहीं लगा सकता है। आपको उपयोगकर्ता इनपुट पर openवैसे भी भरोसा नहीं करना चाहिए , लेकिन अब आपको दोगुना सावधान रहने की आवश्यकता है।
विधि

42

यहाँ मेरे रूबी http का उपयोग करके फाइल करना है open(name, *rest, &block)

require "open-uri"
require "fileutils"

def download(url, path)
  case io = open(url)
  when StringIO then File.open(path, 'w') { |f| f.write(io) }
  when Tempfile then io.close; FileUtils.mv(io.path, path)
  end
end

यहां इसका मुख्य लाभ संक्षिप्त और सरल है, क्योंकि openयह बहुत अधिक भार उठाता है ।और यह स्मृति में पूरी प्रतिक्रिया नहीं पढ़ता है।

openविधि प्रतिक्रियाओं स्ट्रीम होगा> एक करने के लिए 1KB Tempfile। हम इस ज्ञान को इस दुबले डाउनलोड को फ़ाइल पद्धति में लागू करने के लिए उपयोग कर सकते हैं। OpenURI::Bufferकार्यान्वयन यहाँ देखें ।

कृपया उपयोगकर्ता द्वारा प्रदान किए गए इनपुट से सावधान रहें! उपयोगकर्ता इनपुट से आ रहा है open(name, *rest, &block)तो असुरक्षित nameहै!


4
यह स्वीकृत उत्तर होना चाहिए क्योंकि यह संक्षिप्त और सरल है और मेमोरी में पूरी फ़ाइल को लोड नहीं करता है ~ + प्रदर्शन (यहाँ प्रत्यक्ष)।
१३:१६ पर निकोलसगप

मैं Nikkolasg से सहमत हूं। मैंने सिर्फ इसका इस्तेमाल करने की कोशिश की और यह बहुत अच्छा काम करता है। मैंने इसे थोड़ा संशोधित किया, उदाहरण के लिए, स्थानीय पथ दिए गए URL से स्वचालित रूप से कट जाएगा, इसलिए "path = nil" और फिर nil के लिए जाँच करना; अगर यह शून्य है, तो मैं स्थानीय पथ को कम करने के लिए url पर File.basename () का उपयोग करता हूं।
Shevy

1
यह सबसे अच्छा जवाब होगा, लेकिन खुले uri करता स्मृति में पूरे फ़ाइल लोड stackoverflow.com/questions/17454956/...
साइमन Perepelitsa

2
@SimonPerepelitsa हे। मैंने इसे फिर से संशोधित किया, अब एक संक्षिप्त डाउनलोड-टू-फ़ाइल विधि प्रदान करता है जो मेमोरी में पूरी प्रतिक्रिया नहीं पढ़ता है । मेरा पिछला उत्तर पर्याप्त होगा, क्योंकि openवास्तव में स्मृति में प्रतिक्रिया नहीं पढ़ती है, यह किसी भी प्रतिक्रिया के लिए एक अस्थायी फ़ाइल में पढ़ता है> 10240 बाइट्स। तो आप दयालु थे, लेकिन सही नहीं। संशोधित जवाब इस गलतफहमी को साफ करता है और उम्मीद है कि रूबी की शक्ति पर एक महान उदाहरण के रूप में कार्य करता है :)
Overbryd

3
यदि EACCES: permission deniedफ़ाइल नाम को बदलते समय आपको कोई त्रुटि मिलती है, तो आपको mvफ़ाइल को पहले बंद करना होगा। उस भाग को बदलने का सुझावTempfile then io.close;
डेविड डगलस

28

उदाहरण 3 रूबी के नेट / http डॉक्यूमेंटेशन में दिखाया गया है कि कैसे HTTP पर एक डॉक्यूमेंट डाउनलोड करना है, और फाइल को सिर्फ मेमोरी में लोड करने के बजाय, विकल्प को बाइनरी राइट के साथ फाइल में डालता है, जैसे कि Dejw के उत्तर में दिखाया गया है।

उसी दस्तावेज में अधिक जटिल मामलों को और नीचे दिखाया गया है।


मौजूदा प्रलेखन और आगे के उदाहरणों की ओर इशारा करने के लिए +1।
सेम्परोस

1
यहाँ विशेष रूप से लिंक दिया गया है: ruby-doc.org/stdlib-2.1.4/libdoc/net/http/rdoc/Net/……
kgilpin

26

आप ओपन-यूरी का उपयोग कर सकते हैं, जो एक लाइनर है

require 'open-uri'
content = open('http://example.com').read

या नेट / http का उपयोग करके

require 'net/http'
File.write("file_name", Net::HTTP.get(URI.parse("http://url.com")))

10
यह डिस्क में लिखने से पहले पूरी फाइल को मेमोरी में पढ़ता है, ताकि ... वह खराब हो सके।
20

@kgilpin दोनों समाधान?
KrauseFx

1
हाँ, दोनों समाधान।
एलीटायर

उस ने कहा, यदि आप इसके साथ ठीक हैं, तो एक छोटा संस्करण (url और फ़ाइल नाम को चर में urlऔर fileक्रमशः,), open-uriपहले के रूप में उपयोग कर रहे हैं : File.write(file, open(url).read)... मृत सरल, तुच्छ डाउनलोड मामले के लिए।
lindes

17

Dejw के उत्तर पर विस्तार (edit2):

File.open(filename,'w'){ |f|
  uri = URI.parse(url)
  Net::HTTP.start(uri.host,uri.port){ |http| 
    http.request_get(uri.path){ |res| 
      res.read_body{ |seg|
        f << seg
#hack -- adjust to suit:
        sleep 0.005 
      }
    }
  }
}

कहां filenameऔर urlतार हैं।

sleepआदेश एक हैक है कि कर सकते हैं नाटकीय रूप से CPU उपयोग को कम जब नेटवर्क सीमित कारक है। नेट :: HTTP पैदावार से पहले भरने के लिए बफर (v1k.2 में 16kB) का इंतजार नहीं करता है, इसलिए सीपीयू बस खुद ही छोटे-छोटे चक्कों को घुमाता है। एक पल के लिए सो जाने से बफर को लिखने के बीच भरने का मौका मिलता है, और सीपीयू का उपयोग कर्ल समाधान के लिए तुलनीय है, मेरे आवेदन में 4-5x अंतर है। एक अधिक मजबूत समाधान प्रगति की जांच कर सकता हैf.pos और टाइमआउट को लक्ष्य करने के लिए समायोजित कर सकता है, कह सकते हैं, 95% बफर आकार - वास्तव में मुझे अपने उदाहरण में 0.005 नंबर कैसे मिला।

क्षमा करें, लेकिन मुझे नहीं पता कि रूबी के भरने के लिए रूबी की प्रतीक्षा करने का एक और अधिक सुरुचिपूर्ण तरीका है।

संपादित करें:

यह एक ऐसा संस्करण है जो बफर को सिर्फ या उससे कम क्षमता पर रखने के लिए स्वचालित रूप से समायोजित करता है। यह एक अशुभ समाधान है, लेकिन यह सिर्फ उपवास के रूप में प्रतीत होता है, और कम सीपीयू समय का उपयोग करने के लिए, क्योंकि यह कर्ल को बुला रहा है।

यह तीन चरणों में काम करता है। एक जानबूझकर लंबी नींद के समय के साथ एक संक्षिप्त सीखने की अवधि एक पूर्ण बफर के आकार को स्थापित करती है। ड्रॉप अवधि प्रत्येक पुनरावृत्ति के साथ नींद के समय को जल्दी से कम कर देती है, इसे एक बड़े कारक द्वारा गुणा करके, जब तक कि यह एक भरा-भरा बफर न मिल जाए। फिर, सामान्य अवधि के दौरान, यह एक छोटे कारक द्वारा ऊपर और नीचे समायोजित करता है।

मेरी रूबी थोड़ी रूखी है, इसलिए मुझे यकीन है कि इस पर सुधार किया जा सकता है। सबसे पहले, कोई त्रुटि से निपटने नहीं है। इसके अलावा, हो सकता है कि इसे किसी ऑब्जेक्ट में अलग किया जा सके, डाउनलोडिंग से दूर, ताकि आप autosleep.sleep(f.pos)अपने लूप में कॉल करें ? इससे भी बेहतर, नेट :: HTTP पैदावार से पहले एक पूर्ण बफर के लिए इंतजार करने के लिए बदला जा सकता है :-)

def http_to_file(filename,url,opt={})
  opt = {
    :init_pause => 0.1,    #start by waiting this long each time
                           # it's deliberately long so we can see 
                           # what a full buffer looks like
    :learn_period => 0.3,  #keep the initial pause for at least this many seconds
    :drop => 1.5,          #fast reducing factor to find roughly optimized pause time
    :adjust => 1.05        #during the normal period, adjust up or down by this factor
  }.merge(opt)
  pause = opt[:init_pause]
  learn = 1 + (opt[:learn_period]/pause).to_i
  drop_period = true
  delta = 0
  max_delta = 0
  last_pos = 0
  File.open(filename,'w'){ |f|
    uri = URI.parse(url)
    Net::HTTP.start(uri.host,uri.port){ |http|
      http.request_get(uri.path){ |res|
        res.read_body{ |seg|
          f << seg
          delta = f.pos - last_pos
          last_pos += delta
          if delta > max_delta then max_delta = delta end
          if learn <= 0 then
            learn -= 1
          elsif delta == max_delta then
            if drop_period then
              pause /= opt[:drop_factor]
            else
              pause /= opt[:adjust]
            end
          elsif delta < max_delta then
            drop_period = false
            pause *= opt[:adjust]
          end
          sleep(pause)
        }
      }
    }
  }
end

मुझे sleepहैक पसंद है !
Radek

13

Net::HTTPउदाहरण के लिए, प्रतिरूप की तुलना में अधिक एपीआई-अनुकूल पुस्तकालय हैं :

require "httparty"
File.open("/tmp/my_file.flv", "wb") do |f| 
  f.write HTTParty.get("http://somedomain.net/flv/sample/sample.flv").parsed_response
end

3

मुझे समस्या थी, यदि फ़ाइल में जर्मन उमलाट्स (ä, ö, ü) सम्‍मिलित हैं। मैं समस्या को हल करके उपयोग कर सकता हूं:

ec = Encoding::Converter.new('iso-8859-1', 'utf-8')
...
f << ec.convert(seg)
...

0

यदि आप एक तरीका खोज रहे हैं कि कैसे अस्थायी फ़ाइल डाउनलोड करें, तो सामान करें और इसे हटा दें इस रत्न की कोशिश करें https://github.com/equivalent/pull_tempfile

require 'pull_tempfile'

PullTempfile.transaction(url: 'https://mycompany.org/stupid-csv-report.csv', original_filename: 'dont-care.csv') do |tmp_file|
  CSV.foreach(tmp_file.path) do |row|
    # ....
  end
end
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.