扩展 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 命令是一种在网络成为限制因素时可以显着降低 CPU 使用率的 hack。 Net::HTTP 不会等待缓冲区(v1.9.2 中为 16kB)在屈服之前填满,因此 CPU 忙于自己移动小块。休眠片刻让缓冲区有机会在写入之间填充,CPU 使用率与 curl 解决方案相当,在我的应用程序中相差 4-5 倍。一个更强大的解决方案可能会检查 f.pos 的进度,并将超时调整为目标,例如,缓冲区大小的 95%——事实上,这就是我在示例中得到 0.005 数字的方式。
抱歉,我不知道让 Ruby 等待缓冲区填满的更优雅的方法。
编辑:
这是一个自动调整自身以使缓冲区保持在容量或低于容量的版本。这是一个不优雅的解决方案,但它似乎和卷曲一样快,并且使用的 CPU 时间也一样少。
它分三个阶段工作。带有故意长睡眠时间的短暂学习期确定了完整缓冲区的大小。丢弃周期通过将其乘以一个更大的因子来快速减少每次迭代的睡眠时间,直到它找到一个未填充的缓冲区。然后,在正常期间,它会上下调整一个较小的系数。
我的 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