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