Ma solution est fortement basée sur snippets.dzone.com/posts/show/2469 qui est apparu après avoir tapé le téléchargement de fichier ruby dans la barre d'adresse FireFox ... alors avez-vous fait des recherches sur Internet avant de poser cette question?
Dawid
@Dejw: J'ai fait des recherches et j'ai trouvé une réponse ici. Fondamentalement, avec le même code que vous m'avez donné. La resp.bodypartie me déroute, je pensais que cela ne sauverait que la partie «corps» de la réponse mais je veux enregistrer le fichier entier / binaire. J'ai également trouvé que rio.rubyforge.org pouvait être utile. De plus, avec ma question, personne ne peut dire qu'une telle question n'a pas encore été répondue :-)
Radek
3
La partie du corps est exactement le fichier entier. La réponse est créée à partir des en-têtes (http) et du corps (le fichier), donc lorsque vous enregistrez le corps, vous avez enregistré le fichier ;-)
Dawid
1
une autre question ... disons que le fichier fait 100 Mo et que le processus de téléchargement est interrompu au milieu. Y aura-t-il quelque chose de sauvé? Puis-je reprendre le fichier?
Radek
Malheureusement non, car l' http.get('...')appel envoie une requête et reçoit une réponse (le fichier entier). Pour télécharger un fichier en morceaux et l'enregistrer simultanément, voir ma réponse modifiée ci-dessous ;-) La reprise n'est pas facile, peut-être que vous comptez les octets que vous avez enregistrés et que vous les ignorez lorsque vous retéléchargez le fichier ( file.write(resp.body)retourne le nombre d'octets écrits).
Dawid
Réponses:
143
Le moyen le plus simple est la solution spécifique à la plateforme:
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."
Edit: changé. Merci.
Edit2: La solution qui enregistre une partie d'un fichier lors du téléchargement:
# 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
Oui je sais. C'est pourquoi j'ai dit que c'était le cas a platform-specific solution.
Dawid
1
Des solutions plus spécifiques aux plates-formes: les plates-formes GNU / Linux fournissent wget. OS X fournit curl( curl http://oh.no/its/pbjellytime.flv --output secretlylove.flv). Windows a un équivalent Powershell (new-object System.Net.WebClient).DownloadFile('http://oh.no/its/pbjellytime.flv','C:\tmp\secretlylove.flv'). Des fichiers binaires pour wget et curl existent également pour tous les systèmes d'exploitation via téléchargement. Je recommande toujours fortement d'utiliser la bibliothèque standard à moins que votre code d'écriture soit uniquement pour votre propre amour.
fny
1
le début ... assurer ... la fin n'est pas nécessaire si le formulaire de bloc ouvert est utilisé. ouvrez 'sample.flv' faire | f | .... f.write segment
lab419
1
Le fichier non texte arrive corrompu.
Paul
1
J'utilise le téléchargement fragmenté en utilisant Net::HTTP. Et je reçois la partie du fichier mais j'obtiens une réponse Net::HTTPOK. Existe-t-il un moyen de nous assurer que nous avons complètement téléchargé le fichier?
Nickolay Kondratenko
118
Je sais que c'est une vieille question, mais Google m'a jeté ici et je pense avoir trouvé une réponse plus simple.
Dans Railscasts # 179 , Ryan Bates a utilisé la classe standard Ruby OpenURI pour faire une grande partie de ce qui a été demandé comme ceci:
( Avertissement : code non testé. Vous devrez peut-être le modifier / le peaufiner.)
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')ouvrira l'URL en mode binaire.
zoli
1
quelqu'un sait si open-uri est intelligent pour remplir le tampon comme @Isa l'a expliqué?
gdelfino
1
@gildefino Vous obtiendrez plus de réponses si vous ouvrez une nouvelle question pour cela. Il est peu probable que beaucoup de gens lisent ceci (et c'est aussi la chose appropriée à faire dans Stack Overflow).
FWIW certaines personnes pensent que open-uri est dangereux car il monkeypatche tout le code, y compris le code de bibliothèque, qui utilise openune nouvelle capacité que le code appelant pourrait ne pas anticiper. Vous ne devriez pas faire confiance aux entrées utilisateur transmises de opentoute façon, mais vous devez être doublement prudent maintenant.
Le principal avantage ici est concis et simple, car openfait une grande partie du travail lourd.Et il ne lit pas toute la réponse en mémoire.
La openméthode diffusera les réponses> 1 ko vers un fichier Tempfile. Nous pouvons exploiter ces connaissances pour implémenter cette méthode de téléchargement simplifié dans un fichier. Voir leOpenURI::Buffer implémentation ici.
Soyez prudent avec les entrées fournies par l'utilisateur!
open(name, *rest, &block)est dangereux s'il nameprovient d'une entrée utilisateur!
Cela devrait être la réponse acceptée car elle est concise et simple et ne charge pas le fichier entier en mémoire ~ + performances (estimation ici).
Nikkolasg le
Je suis d'accord avec Nikkolasg. J'ai juste essayé de l'utiliser et cela fonctionne très bien. Je l'ai un peu modifié cependant, par exemple, le chemin local sera déduit automatiquement de l'URL donnée, donc par exemple "path = nil" puis en vérifiant nil; s'il est nul, j'utilise File.basename () sur l'url pour déduire le chemin local.
@SimonPerepelitsa hehe. Je l'ai encore révisé, fournissant maintenant une méthode de téléchargement concise dans un fichier qui ne lit pas toute la réponse en mémoire. Ma réponse précédente aurait été suffisante, car openen fait ne lit pas la réponse en mémoire, il la lit dans un fichier temporaire pour toute réponse> 10240 octets. Vous aviez donc raison, mais pas. La réponse révisée nettoie ce malentendu et sert, espérons-le, de bon exemple sur la puissance de Ruby :)
Overbryd
3
Si vous obtenez une EACCES: permission deniederreur lors de la modification du nom de fichier avec la mvcommande, c'est parce que vous devez d'abord fermer le fichier. Suggérer de changer cette partie enTempfile then io.close;
David Douglas
28
Exemple 3 dans la documentation de Ruby's net / http montre comment télécharger un document via HTTP, et pour sortir le fichier au lieu de simplement le charger en mémoire, remplacez-le par une écriture binaire dans un fichier, par exemple comme indiqué dans la réponse de Dejw.
Des cas plus complexes sont présentés plus bas dans le même document.
Cela lit le fichier entier en mémoire avant de l'écrire sur le disque, donc ... cela peut être mauvais.
kgilpin
@kgilpin les deux solutions?
KrauseFx
1
Oui, les deux solutions.
eltiare
Cela dit, si cela vous convient, une version plus courte (en supposant que l'url et le nom de fichier sont dans des variables urlet file, respectivement), en utilisant open-uricomme dans la première: File.write(file, open(url).read)... Dead simple, pour le cas de téléchargement trivial.
lindes
17
Développement sur la réponse de 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}}}}
où filenameeturl sont des chaînes.
La sleepcommande est un hack qui peut réduire considérablement l'utilisation du processeur lorsque le réseau est le facteur limitant. Net :: HTTP n'attend pas que le tampon (16 Ko dans la v1.9.2) se remplisse avant de céder, donc le processeur s'emploie à déplacer de petits morceaux. Dormir pendant un moment donne au tampon une chance de se remplir entre les écritures, et l'utilisation du processeur est comparable à une solution curl, différence 4-5x dans mon application. Une solution plus robuste pourrait examiner les progrèsf.pos et ajuster le délai d'expiration pour cibler, par exemple, 95% de la taille de la mémoire tampon - en fait, c'est ainsi que j'ai obtenu le nombre 0,005 dans mon exemple.
Désolé, mais je ne connais pas une manière plus élégante de faire attendre Ruby que le tampon se remplisse.
Éditer:
Il s'agit d'une version qui s'ajuste automatiquement pour garder le tampon juste à sa capacité ou en dessous. C'est une solution inélégante, mais elle semble être tout aussi rapide et utiliser aussi peu de temps CPU, car elle appelle à curl.
Cela fonctionne en trois étapes. Une brève période d'apprentissage avec un temps de sommeil délibérément long détermine la taille d'un tampon plein. La période de suppression réduit rapidement le temps de sommeil à chaque itération, en le multipliant par un facteur plus grand, jusqu'à ce qu'il trouve un tampon sous-rempli. Ensuite, pendant la période normale, il s'ajuste vers le haut et vers le bas par un facteur plus petit.
Mon Ruby est un peu rouillé, donc je suis sûr que cela peut être amélioré. Tout d'abord, il n'y a pas de gestion des erreurs. Aussi, peut-être qu'il pourrait être séparé en un objet, loin du téléchargement lui-même, de sorte que vous l'appeliez simplement autosleep.sleep(f.pos)dans votre boucle? Mieux encore, Net :: HTTP pourrait être modifié pour attendre un tampon plein avant de céder :-)
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
resp.body
partie me déroute, je pensais que cela ne sauverait que la partie «corps» de la réponse mais je veux enregistrer le fichier entier / binaire. J'ai également trouvé que rio.rubyforge.org pouvait être utile. De plus, avec ma question, personne ne peut dire qu'une telle question n'a pas encore été répondue :-)http.get('...')
appel envoie une requête et reçoit une réponse (le fichier entier). Pour télécharger un fichier en morceaux et l'enregistrer simultanément, voir ma réponse modifiée ci-dessous ;-) La reprise n'est pas facile, peut-être que vous comptez les octets que vous avez enregistrés et que vous les ignorez lorsque vous retéléchargez le fichier (file.write(resp.body)
retourne le nombre d'octets écrits).Réponses:
Le moyen le plus simple est la solution spécifique à la plateforme:
Vous recherchez probablement:
Edit: changé. Merci.
Edit2: La solution qui enregistre une partie d'un fichier lors du téléchargement:
la source
a platform-specific solution
.wget
. OS X fournitcurl
(curl http://oh.no/its/pbjellytime.flv --output secretlylove.flv
). Windows a un équivalent Powershell(new-object System.Net.WebClient).DownloadFile('http://oh.no/its/pbjellytime.flv','C:\tmp\secretlylove.flv')
. Des fichiers binaires pour wget et curl existent également pour tous les systèmes d'exploitation via téléchargement. Je recommande toujours fortement d'utiliser la bibliothèque standard à moins que votre code d'écriture soit uniquement pour votre propre amour.Net::HTTP
. Et je reçois la partie du fichier mais j'obtiens une réponseNet::HTTPOK
. Existe-t-il un moyen de nous assurer que nous avons complètement téléchargé le fichier?Je sais que c'est une vieille question, mais Google m'a jeté ici et je pense avoir trouvé une réponse plus simple.
Dans Railscasts # 179 , Ryan Bates a utilisé la classe standard Ruby OpenURI pour faire une grande partie de ce qui a été demandé comme ceci:
( Avertissement : code non testé. Vous devrez peut-être le modifier / le peaufiner.)
la source
open("http://somedomain.net/flv/sample/sample.flv", 'rb')
ouvrira l'URL en mode binaire.HTTP
=>HTTPS
redirection, et j'ai découvert comment le résoudre en utilisantopen_uri_redirections
Gemopen
une nouvelle capacité que le code appelant pourrait ne pas anticiper. Vous ne devriez pas faire confiance aux entrées utilisateur transmises deopen
toute façon, mais vous devez être doublement prudent maintenant.Voici mon Ruby http à déposer en utilisant
open(name, *rest, &block)
.Le principal avantage ici est concis et simple, car
open
fait une grande partie du travail lourd.Et il ne lit pas toute la réponse en mémoire.La
open
méthode diffusera les réponses> 1 ko vers un fichierTempfile
. Nous pouvons exploiter ces connaissances pour implémenter cette méthode de téléchargement simplifié dans un fichier. Voir leOpenURI::Buffer
implémentation ici.Soyez prudent avec les entrées fournies par l'utilisateur!
open(name, *rest, &block)
est dangereux s'ilname
provient d'une entrée utilisateur!la source
open
en fait ne lit pas la réponse en mémoire, il la lit dans un fichier temporaire pour toute réponse> 10240 octets. Vous aviez donc raison, mais pas. La réponse révisée nettoie ce malentendu et sert, espérons-le, de bon exemple sur la puissance de Ruby :)EACCES: permission denied
erreur lors de la modification du nom de fichier avec lamv
commande, c'est parce que vous devez d'abord fermer le fichier. Suggérer de changer cette partie enTempfile then io.close;
Exemple 3 dans la documentation de Ruby's net / http montre comment télécharger un document via HTTP, et pour sortir le fichier au lieu de simplement le charger en mémoire, remplacez-le par une écriture binaire dans un fichier, par exemple comme indiqué dans la réponse de Dejw.
Des cas plus complexes sont présentés plus bas dans le même document.
la source
Vous pouvez utiliser open-uri, qui est une ligne unique
Ou en utilisant net / http
la source
url
etfile
, respectivement), en utilisantopen-uri
comme dans la première:File.write(file, open(url).read)
... Dead simple, pour le cas de téléchargement trivial.Développement sur la réponse de Dejw (edit2):
où
filename
eturl
sont des chaînes.La
sleep
commande est un hack qui peut réduire considérablement l'utilisation du processeur lorsque le réseau est le facteur limitant. Net :: HTTP n'attend pas que le tampon (16 Ko dans la v1.9.2) se remplisse avant de céder, donc le processeur s'emploie à déplacer de petits morceaux. Dormir pendant un moment donne au tampon une chance de se remplir entre les écritures, et l'utilisation du processeur est comparable à une solution curl, différence 4-5x dans mon application. Une solution plus robuste pourrait examiner les progrèsf.pos
et ajuster le délai d'expiration pour cibler, par exemple, 95% de la taille de la mémoire tampon - en fait, c'est ainsi que j'ai obtenu le nombre 0,005 dans mon exemple.Désolé, mais je ne connais pas une manière plus élégante de faire attendre Ruby que le tampon se remplisse.
Éditer:
Il s'agit d'une version qui s'ajuste automatiquement pour garder le tampon juste à sa capacité ou en dessous. C'est une solution inélégante, mais elle semble être tout aussi rapide et utiliser aussi peu de temps CPU, car elle appelle à curl.
Cela fonctionne en trois étapes. Une brève période d'apprentissage avec un temps de sommeil délibérément long détermine la taille d'un tampon plein. La période de suppression réduit rapidement le temps de sommeil à chaque itération, en le multipliant par un facteur plus grand, jusqu'à ce qu'il trouve un tampon sous-rempli. Ensuite, pendant la période normale, il s'ajuste vers le haut et vers le bas par un facteur plus petit.
Mon Ruby est un peu rouillé, donc je suis sûr que cela peut être amélioré. Tout d'abord, il n'y a pas de gestion des erreurs. Aussi, peut-être qu'il pourrait être séparé en un objet, loin du téléchargement lui-même, de sorte que vous l'appeliez simplement
autosleep.sleep(f.pos)
dans votre boucle? Mieux encore, Net :: HTTP pourrait être modifié pour attendre un tampon plein avant de céder :-)la source
sleep
hack!Il existe plus de bibliothèques compatibles avec les API que
Net::HTTP
, par exemple, httparty :la source
J'ai eu des problèmes, si le fichier contenait des Umlauts allemands (ä, ö, ü). Je pourrais résoudre le problème en utilisant:
la source
si vous cherchez un moyen de télécharger un fichier temporaire, faites des choses et supprimez-le, essayez cette gemme https://github.com/equivalent/pull_tempfile
la source