Расширение ответа Дейва (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
}
}
}
}
, где filename
и url
- строки.
Команда sleep
- это хак, который может значительно снизить нагрузку на процессор, когда ограничивающим фактором является сеть. Net :: HTTP не ждет, пока буфер (16 КБ в v1.9.2) заполнится, прежде чем уступить, поэтому процессор занят, перемещая небольшие куски. Спящий момент дает буферу возможность заполняться между записями, а загрузка ЦП сравнима с решением для скручивания, разница в 4-5 раз в моем приложении. Более надежное решение может проверить ход выполнения f.pos
и скорректировать время ожидания, например, на 95% от размера буфера - фактически, так я получил число 0,005 в моем примере.
Извините, но я не знаю более элегантного способа заставить Руби ждать, пока буфер заполнится.
Edit:
Это версия, которая автоматически настраивается для поддержания буфера на уровне или ниже. Это не элегантное решение, но оно кажется таким же быстрым и потребляет так мало процессорного времени, как оно требует скручивания.
Работает в три этапа. Краткий период обучения с преднамеренно длительным временем сна устанавливает размер полного буфера. Период отбрасывания сокращает время ожидания с каждой итерацией, умножая его на больший коэффициент, пока не найдет недостаточно заполненный буфер. Затем в течение нормального периода он настраивается вверх и вниз с меньшим коэффициентом.
Мой Руби немного ржавый, так что я уверен, что это можно улучшить. Прежде всего, нет обработки ошибок. Кроме того, может быть, он мог бы быть разделен на объект, отдельно от самой загрузки, чтобы вы просто вызывали autosleep.sleep(f.pos)
в своем цикле? Более того, Net :: HTTP можно изменить так, чтобы он ожидал полного буфера перед выдачей: -)
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