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 cuerdas.
El sleep
comando 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 pequeños trozos. Dormir por un momento le da al búfer la oportunidad de llenarse entre escrituras, y el uso de la CPU es comparable a una solución curl, con una diferencia de 4-5x en mi aplicación. Una solución más sólida podría examinar el progreso f.pos
y ajustar el tiempo de espera para alcanzar, 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 hacer que Ruby espere a que se llene el búfer.
Editar:
Esta es una versión que se ajusta automáticamente para mantener el búfer a la capacidad o por debajo. Es una solución poco elegante, pero parece ser tan rápida y usar tan poco tiempo de CPU, como se dice que se encrespa.
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 suspensión rápidamente con cada iteración, multiplicándolo por un factor mayor, hasta que encuentra un búfer insuficientemente lleno. Luego, durante el período normal, se ajusta hacia arriba y hacia abajo en 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 en sí, para que simplemente llame autosleep.sleep(f.pos)
a su bucle? Aún mejor, Net :: HTTP podría cambiarse para esperar un búfer completo antes de producir :-)
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