Comment télécharger et enregistrer un fichier binaire sur HTTP à l'aide de Ruby?
L'URL est http://somedomain.net/flv/sample/sample.flv
.
Je suis sur la plate-forme Windows et je préférerais ne pas exécuter de programme externe.
Le moyen le plus simple est la solution spécifique à la plate-forme:
#!/usr/bin/env Ruby
`wget http://somedomain.net/flv/sample/sample.flv`
Vous recherchez probablement:
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."
Edit: modifié. Merci.
Edit2: La solution qui enregistre une partie du 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)
end
end
ensure
f.close()
end
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é le Ruby classe standard OpenURI ] pour faire en grande partie ce qui a été demandé comme ceci:
( Warning : code non testé. Vous devrez peut-être le modifier/le modifier.)
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
Voici mon Ruby http dans un fichier utilisant 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
Le principal avantage ici est qu’il est concis et simple, car open
s’occupe beaucoup des tâches lourdes. Et il ne lit pas l'intégralité de la réponse en mémoire.
La méthode open
transmettra les réponses> 1 Ko à Tempfile
. Nous pouvons exploiter cette connaissance pour implémenter cette méthode de téléchargement au fichier simplifiée. Voir la mise en œuvre OpenURI::Buffer
ici.
S'il vous plaît soyez prudent avec l'entrée fournie par l'utilisateur! open(name, *rest, &block)
est dangereux si name
provient d'une entrée utilisateur!
L'exemple 3 de la documentation net/http de Ruby montre comment télécharger un document via HTTP et comment exporter le fichier au lieu de le charger en mémoire, remplacez la place par une écriture binaire dans un fichier, par exemple. comme indiqué dans la réponse de Dejw.
Les cas plus complexes sont présentés plus bas dans le même document.
Vous pouvez utiliser open-uri, qui est un one-liner
require 'open-uri'
content = open('http://example.com').read
Ou en utilisant net/http
require 'net/http'
File.write("file_name", Net::HTTP.get(URI.parse("http://url.com")))
Développer 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ù filename
et url
sont des chaînes.
La commande sleep
est un hack qui peut dramatiquement réduire l'utilisation du processeur lorsque le réseau est le facteur limitant. Net :: HTTP n'attend pas que la mémoire tampon (16 Ko dans la version 1.9.2) se remplisse avant de céder, de sorte que le processeur se déplace lui-même. Dormir un instant donne au tampon une chance de se remplir entre les écritures, et l'utilisation du processeur est comparable à une solution curl, une différence de 4-5x dans mon application. Une solution plus robuste pourrait examiner les progrès de f.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 de façon plus élégante d’avoir Ruby attendre que le tampon se remplisse.
Modifier:
Il s’agit d’une version qui s’ajuste automatiquement pour maintenir la mémoire tampon au maximum ou au-dessous de sa capacité. C'est une solution peu élégante, mais elle semble être tout aussi rapide et utiliser peu de temps de calcul que l'appelant curl.
Cela fonctionne en trois étapes. Une brève période d'apprentissage avec une période de sommeil délibérément longue établit la taille d'un tampon complet. La période de largage réduit rapidement le temps de sommeil à chaque itération, en le multipliant par un facteur plus important, jusqu'à trouver un tampon sous-rempli. Ensuite, pendant la période normale, il s’ajuste 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 traitement d'erreur. En outre, peut-être pourrait-il être séparé en un objet, loin du téléchargement lui-même, pour que vous appeliez simplement autosleep.sleep(f.pos)
dans votre boucle? Mieux encore, Net :: HTTP pourrait être modifié pour attendre un tampon complet 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 = 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
Il y a plus de bibliothèques conviviales à l'API que Net::HTTP
_, par exemple httparty :
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
J'ai eu des problèmes, si le fichier contenait des trémas allemands (ä, ö, ü). Je pourrais résoudre le problème en utilisant:
ec = Encoding::Converter.new('iso-8859-1', 'utf-8')
...
f << ec.convert(seg)
...
si vous cherchez un moyen de télécharger un fichier temporaire, de faire des choses et de le supprimer, essayez cette gemme 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