Розгортання відповіді 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
}
}
}
}
де filename
і url
є рядки.
sleep
Команда хак , який може значно зменшити завантаження процесора , коли мережа є обмежуючим фактором. Net :: HTTP не чекає, поки буфер заповнить буфер (16 кБ в v1.9.2), тому CPU сам займається переміщенням невеликих шматочків. Сон на хвилину дає буферу можливість заповнити записи, а використання процесора порівнянне з рішенням curl, різниця в моєму застосуванні на 4-5 разів. Більш надійне рішення може вивчити хід f.pos
та налаштувати тайм-аут для орієнтації, скажімо, на 95% розміру буфера - адже саме так я отримав число 0,005 у своєму прикладі.
Вибачте, але я не знаю більш елегантного способу змусити Рубі чекати, коли буфер заповниться.
Редагувати:
Це версія, яка автоматично налаштовує себе на те, щоб утримувати буфер трохи нижче та нижче. Це неелегантне рішення, але воно, здається, настільки ж швидко, і використовувати якнайменше часу процесора, оскільки це вимагає згортання.
Він працює в три етапи. Короткий період навчання з навмисно довгим часом сну встановлює розмір повного буфера. Період краплі швидко скорочує час сну з кожною ітерацією, помножуючи його на більший коефіцієнт, доки він не знайде недостатньо заповнений буфер. Потім, у звичайний період, вона регулюється вгору і вниз меншим фактором.
Мій Рубі трохи іржавий, тому я впевнений, що це можна покращити. Перш за все, немає помилок в обробці. Також, можливо, його можна було б відокремити в об'єкт, подалі від самого завантаження, щоб ви просто зателефонували 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