Meine Lösung basiert stark auf snippets.dzone.com/posts/show/2469, das angezeigt wurde, nachdem ich den Download von Ruby-Dateien in die FireFox-Adressleiste eingegeben habe. Haben Sie also im Internet recherchiert, bevor Sie diese Frage gestellt haben?
Dawid
@Dejw: Ich habe recherchiert und hier eine beantwortete Frage gefunden. Grundsätzlich mit dem gleichen Code, den Sie mir gegeben haben. Der resp.bodyTeil verwirrt mich. Ich dachte, er würde nur den 'Körper'-Teil der Antwort speichern, aber ich möchte die gesamte / binäre Datei speichern. Ich fand auch, dass rio.rubyforge.org hilfreich sein könnte. Außerdem kann mit meiner Frage niemand sagen, dass eine solche Frage noch nicht beantwortet wurde :-)
Radek
3
Der Körperteil ist genau die ganze Datei. Die Antwort wird aus den Headern (http) und dem Body (der Datei) erstellt. Wenn Sie also den Body speichern, haben Sie die Datei gespeichert ;-)
Dawid
1
Noch eine Frage ... Nehmen wir an, die Datei ist 100 MB groß und der Download-Vorgang wird in der Mitte unterbrochen. Wird etwas gerettet? Kann ich die Datei wieder aufnehmen?
Radek
Leider nicht, da der http.get('...')Anruf eine Anfrage sendet und eine Antwort empfängt (die gesamte Datei). Um eine Datei in Blöcken herunterzuladen und gleichzeitig zu speichern, siehe meine bearbeitete Antwort unten ;-) Das Fortsetzen ist nicht einfach. Vielleicht zählen Sie die gespeicherten Bytes und überspringen sie, wenn Sie die Datei erneut herunterladen ( file.write(resp.body)gibt die Anzahl der geschriebenen Bytes zurück).
Dawid
Antworten:
143
Der einfachste Weg ist die plattformspezifische Lösung:
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)endend
puts "Done."
Bearbeiten: Geändert. Danke.
Edit2: Die Lösung, die einen Teil einer Datei beim Herunterladen speichert:
# instead of http.get
f = open('sample.flv')begin
http.request_get('/sample.flv')do|resp|
resp.read_body do|segment|
f.write(segment)endendensure
f.close()end
Ja, ich weiß. Deshalb habe ich gesagt, dass es so ist a platform-specific solution.
Dawid
1
Weitere plattformspezifische Lösungen: GNU / Linux-Plattformen bieten wget. OS X bietet curl( curl http://oh.no/its/pbjellytime.flv --output secretlylove.flv). Windows hat ein Powershell-Äquivalent (new-object System.Net.WebClient).DownloadFile('http://oh.no/its/pbjellytime.flv','C:\tmp\secretlylove.flv'). Binärdateien für wget und curl gibt es für alle Betriebssysteme auch per Download. Ich empfehle weiterhin dringend, die Standardbibliothek zu verwenden, es sei denn, Sie schreiben Code ausschließlich für Ihre eigene Liebe.
fny
1
Der Anfang ... stellt sicher, dass das Ende nicht erforderlich ist, wenn das offene Blockformular verwendet wird. öffne 'sample.flv' do | f | .... f.write segment
lab419
1
Die Nicht-Textdatei kommt beschädigt an.
Paul
1
Ich benutze Chunked Download mit Net::HTTP. Und ich erhalte den Teil der Datei, bekomme aber eine Antwort Net::HTTPOK. Gibt es eine Möglichkeit, um sicherzustellen, dass wir die Datei vollständig heruntergeladen haben?
Nickolay Kondratenko
118
Ich weiß, dass dies eine alte Frage ist, aber Google hat mich hierher geworfen und ich denke, ich habe eine einfachere Antwort gefunden.
( Warnung : ungetesteter Code. Möglicherweise müssen Sie ihn ändern / optimieren.)
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)endend
open("http://somedomain.net/flv/sample/sample.flv", 'rb')öffnet die URL im Binärmodus.
Zoli
1
Weiß jemand, ob Open-Uri intelligent darin ist, den Puffer zu füllen, wie @Isa erklärt hat?
Gdelfino
1
@ Gildefino Sie erhalten mehr Antworten, wenn Sie eine neue Frage dazu öffnen. Es ist unwahrscheinlich, dass viele Leute dies lesen (und es ist auch die richtige Sache, um in Stack Overflow zu tun).
FWIW Einige Leute denken, dass Open-Uri gefährlich ist, weil es den gesamten Code, einschließlich des Bibliothekscodes, monkeypatcht, der openmit einer neuen Fähigkeit verwendet wird, die der aufrufende Code möglicherweise nicht vorwegnimmt. Sie sollten openohnehin nicht auf Benutzereingaben vertrauen , aber Sie müssen jetzt doppelt vorsichtig sein.
Der Hauptvorteil hier ist es prägnant und einfach, weil ein openGroßteil des schweren Hebens erledigt.Und es liest nicht die gesamte Antwort im Speicher.
Die openMethode überträgt Antworten> 1 KB an a Tempfile. Wir können dieses Wissen nutzen, um diese Lean-Download-to-File-Methode zu implementieren. Siehe die OpenURI::BufferImplementierung hier.
Bitte seien Sie vorsichtig mit vom Benutzer bereitgestellten Eingaben!
open(name, *rest, &block)ist unsicher, wenn namevon Benutzereingaben kommt!
Dies sollte die akzeptierte Antwort sein, da sie präzise und einfach ist und nicht die gesamte Datei in den Speicher lädt ~ + Leistung (Schätzung hier).
Nikkolasg
Ich stimme Nikkolasg zu. Ich habe gerade versucht, es zu benutzen und es funktioniert sehr gut. Ich habe es ein wenig geändert, zum Beispiel wird der lokale Pfad automatisch von der angegebenen URL abgeleitet, also z. B. "path = nil" und dann nach nil suchen; Wenn es Null ist, verwende ich File.basename () in der URL, um den lokalen Pfad abzuleiten.
@ SimonPerepelitsa hehe. Ich habe es noch einmal überarbeitet und jetzt eine übersichtliche Download-to-File-Methode bereitgestellt, die nicht die gesamte Antwort im Speicher liest . Meine vorherige Antwort wäre ausreichend gewesen, da opendie Antwort tatsächlich nicht im Speicher gelesen wird, sondern für Antworten> 10240 Byte in eine temporäre Datei eingelesen wird. Sie hatten also ein bisschen Recht, aber nicht. Die überarbeitete Antwort räumt dieses Missverständnis auf und dient hoffentlich als
gutes
3
Wenn Sie EACCES: permission deniedbeim Ändern des Dateinamens mit dem mvBefehl eine Fehlermeldung erhalten , müssen Sie die Datei zuerst schließen. Schlagen Sie vor, diesen Teil zu ändernTempfile then io.close;
David Douglas
28
Beispiel 3 in der net / http-Dokumentation von Ruby zeigt, wie Sie ein Dokument über HTTP herunterladen und die Datei ausgeben, anstatt sie nur in den Speicher zu laden. Ersetzen Sie Puts durch ein binäres Schreiben in eine Datei, z. B. wie in Dejws Antwort gezeigt.
Komplexere Fälle werden weiter unten im selben Dokument gezeigt.
Dies liest die gesamte Datei in den Speicher, bevor sie auf die Festplatte geschrieben wird. Das kann also schlecht sein.
kgilpin
@ kgilpin beide Lösungen?
KrauseFx
1
Ja, beide Lösungen.
eltiare
Das heißt, wenn du bist OK mit , dass eine kürzere Version (unter der Annahme , URL und Dateinamen sind in Variablen urlund filejeweils) unter Verwendung von open-uriwie in der ersten: File.write(file, open(url).read)... Toten einfach, für den trivialen Download Fall.
Lindes
17
Erweiterung der Antwort von 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}}}}
wo filenameundurl sind Saiten.
Der sleepBefehl ist ein Hack, der die CPU-Auslastung drastisch reduzieren kann, wenn das Netzwerk der begrenzende Faktor ist. Net :: HTTP wartet nicht darauf, dass der Puffer (16 KB in Version 1.9.2) gefüllt wird, bevor er nachgibt, sodass die CPU selbst kleine Teile bewegt. Wenn Sie einen Moment lang schlafen, kann sich der Puffer zwischen den Schreibvorgängen füllen, und die CPU-Auslastung ist vergleichbar mit einer Curl-Lösung, die sich in meiner Anwendung um das 4-5-fache unterscheidet. Eine robustere Lösung könnte den Fortschritt von untersuchenf.pos und das Zeitlimit anpassen, um beispielsweise 95% der Puffergröße zu erreichen - tatsächlich habe ich in meinem Beispiel die 0,005-Zahl erhalten.
Entschuldigung, aber ich kenne keine elegantere Möglichkeit, Ruby warten zu lassen, bis sich der Puffer gefüllt hat.
Bearbeiten:
Dies ist eine Version, die sich automatisch anpasst, um den Puffer auf oder unter der Kapazität zu halten. Es ist eine unelegante Lösung, aber es scheint genauso schnell zu sein und so wenig CPU-Zeit zu verbrauchen, wie es zum Locken aufruft.
Es funktioniert in drei Stufen. Eine kurze Lernphase mit einer absichtlich langen Schlafzeit legt die Größe eines vollen Puffers fest. Die Drop-Periode reduziert die Ruhezeit bei jeder Iteration schnell, indem sie mit einem größeren Faktor multipliziert wird, bis ein unterfüllter Puffer gefunden wird. Während der normalen Zeit wird es dann um einen kleineren Faktor nach oben und unten angepasst.
Mein Ruby ist etwas rostig, daher bin ich mir sicher, dass dies verbessert werden kann. Erstens gibt es keine Fehlerbehandlung. Vielleicht könnte es auch in ein Objekt getrennt werden, weg vom Herunterladen selbst, so dass Sie einfach autosleep.sleep(f.pos)in Ihrer Schleife aufrufen würden ? Noch besser ist, dass Net :: HTTP geändert werden könnte, um auf einen vollen Puffer zu warten, bevor es ergibt :-)
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 =0File.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 endif learn <=0then
learn -=1elsif delta == max_delta thenif drop_period then
pause /= opt[:drop_factor]else
pause /= opt[:adjust]endelsif delta < max_delta then
drop_period =false
pause *= opt[:adjust]end
sleep(pause)}}}}end
Wenn Sie nach einer Möglichkeit suchen, temporäre Dateien herunterzuladen, Dinge zu tun und sie zu löschen, versuchen Sie dieses Juwel https://github.com/equivalent/pull_tempfile
resp.body
Teil verwirrt mich. Ich dachte, er würde nur den 'Körper'-Teil der Antwort speichern, aber ich möchte die gesamte / binäre Datei speichern. Ich fand auch, dass rio.rubyforge.org hilfreich sein könnte. Außerdem kann mit meiner Frage niemand sagen, dass eine solche Frage noch nicht beantwortet wurde :-)http.get('...')
Anruf eine Anfrage sendet und eine Antwort empfängt (die gesamte Datei). Um eine Datei in Blöcken herunterzuladen und gleichzeitig zu speichern, siehe meine bearbeitete Antwort unten ;-) Das Fortsetzen ist nicht einfach. Vielleicht zählen Sie die gespeicherten Bytes und überspringen sie, wenn Sie die Datei erneut herunterladen (file.write(resp.body)
gibt die Anzahl der geschriebenen Bytes zurück).Antworten:
Der einfachste Weg ist die plattformspezifische Lösung:
Wahrscheinlich suchen Sie nach:
Bearbeiten: Geändert. Danke.
Edit2: Die Lösung, die einen Teil einer Datei beim Herunterladen speichert:
quelle
a platform-specific solution
.wget
. OS X bietetcurl
(curl http://oh.no/its/pbjellytime.flv --output secretlylove.flv
). Windows hat ein Powershell-Äquivalent(new-object System.Net.WebClient).DownloadFile('http://oh.no/its/pbjellytime.flv','C:\tmp\secretlylove.flv')
. Binärdateien für wget und curl gibt es für alle Betriebssysteme auch per Download. Ich empfehle weiterhin dringend, die Standardbibliothek zu verwenden, es sei denn, Sie schreiben Code ausschließlich für Ihre eigene Liebe.Net::HTTP
. Und ich erhalte den Teil der Datei, bekomme aber eine AntwortNet::HTTPOK
. Gibt es eine Möglichkeit, um sicherzustellen, dass wir die Datei vollständig heruntergeladen haben?Ich weiß, dass dies eine alte Frage ist, aber Google hat mich hierher geworfen und ich denke, ich habe eine einfachere Antwort gefunden.
In Railscasts Nr. 179 verwendete Ryan Bates die Ruby-Standardklasse OpenURI , um viele der folgenden Fragen zu beantworten :
( Warnung : ungetesteter Code. Möglicherweise müssen Sie ihn ändern / optimieren.)
quelle
open("http://somedomain.net/flv/sample/sample.flv", 'rb')
öffnet die URL im Binärmodus.HTTP
=>HTTPS
Umleitung und fand heraus, wie man es mitopen_uri_redirections
Gemopen
mit einer neuen Fähigkeit verwendet wird, die der aufrufende Code möglicherweise nicht vorwegnimmt. Sie solltenopen
ohnehin nicht auf Benutzereingaben vertrauen , aber Sie müssen jetzt doppelt vorsichtig sein.Hier ist mein Ruby http zur Datei mit
open(name, *rest, &block)
.Der Hauptvorteil hier ist es prägnant und einfach, weil ein
open
Großteil des schweren Hebens erledigt.Und es liest nicht die gesamte Antwort im Speicher.Die
open
Methode überträgt Antworten> 1 KB an aTempfile
. Wir können dieses Wissen nutzen, um diese Lean-Download-to-File-Methode zu implementieren. Siehe dieOpenURI::Buffer
Implementierung hier.Bitte seien Sie vorsichtig mit vom Benutzer bereitgestellten Eingaben!
open(name, *rest, &block)
ist unsicher, wennname
von Benutzereingaben kommt!quelle
open
die Antwort tatsächlich nicht im Speicher gelesen wird, sondern für Antworten> 10240 Byte in eine temporäre Datei eingelesen wird. Sie hatten also ein bisschen Recht, aber nicht. Die überarbeitete Antwort räumt dieses Missverständnis auf und dient hoffentlich alsEACCES: permission denied
beim Ändern des Dateinamens mit demmv
Befehl eine Fehlermeldung erhalten , müssen Sie die Datei zuerst schließen. Schlagen Sie vor, diesen Teil zu ändernTempfile then io.close;
Beispiel 3 in der net / http-Dokumentation von Ruby zeigt, wie Sie ein Dokument über HTTP herunterladen und die Datei ausgeben, anstatt sie nur in den Speicher zu laden. Ersetzen Sie Puts durch ein binäres Schreiben in eine Datei, z. B. wie in Dejws Antwort gezeigt.
Komplexere Fälle werden weiter unten im selben Dokument gezeigt.
quelle
Sie können Open-Uri verwenden, einen Einzeiler
Oder mit net / http
quelle
url
undfile
jeweils) unter Verwendung vonopen-uri
wie in der ersten:File.write(file, open(url).read)
... Toten einfach, für den trivialen Download Fall.Erweiterung der Antwort von Dejw (edit2):
wo
filename
undurl
sind Saiten.Der
sleep
Befehl ist ein Hack, der die CPU-Auslastung drastisch reduzieren kann, wenn das Netzwerk der begrenzende Faktor ist. Net :: HTTP wartet nicht darauf, dass der Puffer (16 KB in Version 1.9.2) gefüllt wird, bevor er nachgibt, sodass die CPU selbst kleine Teile bewegt. Wenn Sie einen Moment lang schlafen, kann sich der Puffer zwischen den Schreibvorgängen füllen, und die CPU-Auslastung ist vergleichbar mit einer Curl-Lösung, die sich in meiner Anwendung um das 4-5-fache unterscheidet. Eine robustere Lösung könnte den Fortschritt von untersuchenf.pos
und das Zeitlimit anpassen, um beispielsweise 95% der Puffergröße zu erreichen - tatsächlich habe ich in meinem Beispiel die 0,005-Zahl erhalten.Entschuldigung, aber ich kenne keine elegantere Möglichkeit, Ruby warten zu lassen, bis sich der Puffer gefüllt hat.
Bearbeiten:
Dies ist eine Version, die sich automatisch anpasst, um den Puffer auf oder unter der Kapazität zu halten. Es ist eine unelegante Lösung, aber es scheint genauso schnell zu sein und so wenig CPU-Zeit zu verbrauchen, wie es zum Locken aufruft.
Es funktioniert in drei Stufen. Eine kurze Lernphase mit einer absichtlich langen Schlafzeit legt die Größe eines vollen Puffers fest. Die Drop-Periode reduziert die Ruhezeit bei jeder Iteration schnell, indem sie mit einem größeren Faktor multipliziert wird, bis ein unterfüllter Puffer gefunden wird. Während der normalen Zeit wird es dann um einen kleineren Faktor nach oben und unten angepasst.
Mein Ruby ist etwas rostig, daher bin ich mir sicher, dass dies verbessert werden kann. Erstens gibt es keine Fehlerbehandlung. Vielleicht könnte es auch in ein Objekt getrennt werden, weg vom Herunterladen selbst, so dass Sie einfach
autosleep.sleep(f.pos)
in Ihrer Schleife aufrufen würden ? Noch besser ist, dass Net :: HTTP geändert werden könnte, um auf einen vollen Puffer zu warten, bevor es ergibt :-)quelle
sleep
Hack!Es gibt api-freundlichere Bibliotheken als
Net::HTTP
zum Beispiel httparty :quelle
Ich hatte Probleme, wenn die Datei deutsche Umlaute enthielt (ä, ö, ü). Ich könnte das Problem lösen mit:
quelle
Wenn Sie nach einer Möglichkeit suchen, temporäre Dateien herunterzuladen, Dinge zu tun und sie zu löschen, versuchen Sie dieses Juwel https://github.com/equivalent/pull_tempfile
quelle