¿Cómo descargo un archivo binario a través de HTTP?

¿Cómo descargo y guardo un archivo binario a través de HTTP usando Ruby?

La URL es http://somedomain.net/flv/sample/sample.flv .

Estoy en la plataforma de Windows y preferiría no ejecutar ningún progtwig externo.

La forma más sencilla es la solución específica de la plataforma:

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

Probablemente estás buscando:

 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." 

Editar: Cambiado. Gracias.

Edit2: la solución que guarda parte de un archivo durante la descarga:

 # 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 

Sé que esta es una vieja pregunta, pero Google me tiró aquí y creo que encontré una respuesta más simple.

En Railscasts # 179 , Ryan Bates utilizó la clase estándar Ruby OpenURI para hacer mucho de lo que se le preguntó de esta manera:

( Advertencia : código no probado. Es posible que deba cambiarlo o modificarlo).

 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 

Aquí está mi http de Ruby para archivar usando 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 FileUtils.mv(io.path, path) end end 

La principal ventaja aquí es conciso y simple, porque open hace gran parte del trabajo pesado. Y no lee toda la respuesta en la memoria.

El método open transmitirá respuestas> 1kb a un Tempfile . Podemos aprovechar este conocimiento para implementar este método de descarga magra a archivo. Vea la implementación de OpenURI::Buffer aquí.

¡Tenga cuidado con la entrada proporcionada por el usuario! open(name, *rest, &block) no es seguro si el name proviene de la entrada del usuario.

El ejemplo 3 en la documentación de net / http de Ruby muestra cómo descargar un documento a través de HTTP, y para generar el archivo en lugar de solo cargarlo en la memoria, sustituya puts con una escritura binaria en un archivo, por ejemplo, como se muestra en la respuesta de Dejw.

Los casos más complejos se muestran más abajo en el mismo documento.

Puedes usar open-uri, que es un trazador de líneas

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

O usando net / http

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

Ampliando la respuesta 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 } } } } 

donde filename y url son cadenas.

El comando de sleep es un truco que puede reducir drásticamente el uso de la CPU cuando la red es el factor limitante. Net :: HTTP no espera a que se llene el búfer (16kB en v1.9.2) antes de ceder, por lo que la CPU se ocupa de mover trozos pequeños. Dormir por un momento da al búfer la oportunidad de rellenar entre escrituras, y el uso de la CPU es comparable a una solución de rizo, una diferencia de 4 a 5 veces en mi aplicación. Una solución más robusta podría examinar el progreso de f.pos y ajustar el tiempo de espera para alcanzar el objective, por ejemplo, el 95% del tamaño del búfer, de hecho, así es como obtuve el número 0.005 en mi ejemplo.

Lo siento, pero no conozco una forma más elegante de que Ruby espere a que se llene el búfer.

Editar:

Esta es una versión que se ajusta automáticamente para mantener el buffer justo a la capacidad o por debajo de ella. Es una solución poco elegante, pero parece ser igual de rápida, y usa tan poco tiempo de CPU, ya que está llamando a curl.

Funciona en tres etapas. Un breve período de aprendizaje con un tiempo de sueño deliberadamente largo establece el tamaño de un búfer completo. El período de caída reduce el tiempo de inactividad rápidamente con cada iteración, al multiplicarlo por un factor más grande, hasta que encuentra un búfer con poca carga. Luego, durante el período normal, se ajusta hacia arriba y hacia abajo por un factor menor.

Mi Ruby está un poco oxidado, así que estoy seguro de que esto se puede mejorar. En primer lugar, no hay manejo de errores. Además, ¿tal vez podría separarse en un objeto, lejos de la descarga misma, para que usted simplemente llame a autosleep.sleep(f.pos) en su ciclo? Incluso mejor, Net :: HTTP podría cambiarse para esperar un buffer completo antes de ceder 🙂

 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 

Hay más bibliotecas Net::HTTP con Api que Net::HTTP , por ejemplo 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 

Tuve problemas, si el archivo contenía German Umlauts (ä, ö, ü). Pude resolver el problema usando:

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

Si está buscando una forma de descargar archivos temporales, hacer cosas y eliminarlas, pruebe esta joya 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