Expandindo a resposta 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
}
}
}
}
onde filename
eurl
são strings.
O sleep
comando é um hack que pode reduzir drasticamente o uso da CPU quando a rede é o fator limitante. O Net :: HTTP não espera que o buffer (16kB na v1.9.2) seja preenchido antes de render, então a CPU se ocupa movendo pequenos pedaços. Dormir por um momento dá ao buffer a chance de preencher entre gravações e o uso da CPU é comparável a uma solução de curl, diferença de 4-5x no meu aplicativo. Uma solução mais robusta pode examinar o progresso def.pos
e ajustar o tempo limite para atingir, digamos, 95% do tamanho do buffer - na verdade, foi assim que obtive o número 0,005 no meu exemplo.
Desculpe, mas não conheço uma maneira mais elegante de fazer com que Ruby aguarde o buffer preencher.
Editar:
Esta é uma versão que se ajusta automaticamente para manter o buffer igual ou inferior à capacidade. É uma solução deselegante, mas parece ser tão rápida e usar tão pouco tempo de CPU, como está chamando a curl.
Funciona em três etapas. Um breve período de aprendizado com um tempo de sono deliberadamente longo estabelece o tamanho de um buffer completo. O período de queda reduz o tempo de suspensão rapidamente a cada iteração, multiplicando-o por um fator maior, até encontrar um buffer insuficiente. Então, durante o período normal, ele se ajusta para cima e para baixo por um fator menor.
Meu Ruby está um pouco enferrujado, então tenho certeza de que isso pode ser melhorado. Primeiro de tudo, não há tratamento de erros. Além disso, talvez ele possa ser separado em um objeto, longe do próprio download, para que você apenas chame autosleep.sleep(f.pos)
no seu loop? Melhor ainda, o Net :: HTTP pode ser alterado para aguardar um buffer completo antes de produzir :-)
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