Espandendo la risposta di Dejw (modifica2):
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
}
}
}
}
dove filename
e url
sono stringhe.
Il sleep
comando è un hack che può ridurre drasticamente l'utilizzo della CPU quando la rete è il fattore limitante. Net :: HTTP non attende che il buffer (16kB in v1.9.2) si riempia prima di cedere, quindi la CPU si impegna a spostare piccoli pezzi in giro. La sospensione per un momento offre al buffer la possibilità di riempire tra le scritture e l'utilizzo della CPU è paragonabile a una soluzione di arricciatura, con una differenza di 4-5 volte nella mia applicazione. Una soluzione più solida potrebbe esaminare l'avanzamento f.pos
e regolare il timeout per target, diciamo, il 95% della dimensione del buffer - in effetti è così che ho ottenuto il numero 0,005 nel mio esempio.
Scusa, ma non conosco un modo più elegante di far aspettare Ruby per riempire il buffer.
Modificare:
Questa è una versione che si regola automaticamente per mantenere il buffer appena o al di sotto della capacità. È una soluzione non elegante, ma sembra essere altrettanto veloce e utilizza meno tempo della CPU, come sta chiamando per arricciarsi.
Funziona in tre fasi. Un breve periodo di apprendimento con un tempo di sonno deliberatamente lungo stabilisce le dimensioni di un buffer completo. Il periodo di rilascio riduce rapidamente il tempo di sospensione a ogni iterazione, moltiplicandolo per un fattore maggiore, fino a quando non trova un buffer insufficiente. Quindi, durante il periodo normale, si regola su e giù di un fattore minore.
Il mio Ruby è un po 'arrugginito, quindi sono sicuro che questo può essere migliorato. Prima di tutto, non c'è gestione degli errori. Inoltre, forse potrebbe essere separato in un oggetto, lontano dal download stesso, in modo da chiamare semplicemente autosleep.sleep(f.pos)
nel tuo loop? Ancora meglio, Net :: HTTP potrebbe essere modificato per attendere un buffer completo prima di cedere :-)
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