如何通过HTTP下载二进制文件?
如何使用Ruby下载和保存二进制文件通过HTTP?如何通过HTTP下载二进制文件?
URL是http://somedomain.net/flv/sample/sample.flv
。
我在Windows平台上,我不想运行任何外部程序。
最简单的方法是特定于平台的解决方案:
也许你正在寻找:
require 'net/http'
# Must be somedomain.net instead of somedomain.net/, otherwise, it will throw exception.
Net::HTTP.start("somedomain.net") do |http|
resp = http.get("/flv/sample/sample.flv")
open("sample.flv", "wb") do |file|
file.write(resp.body)
end
end
puts "Done."
编辑:改变。谢谢。
EDIT2:
# instead of http.get
f = open('sample.flv')
begin
http.request_get('/sample.flv') do |resp|
resp.read_body do |segment|
f.write(segment)
end
end
ensure
f.close()
end
第一个“简单”解决方法将无法在Windows机器上工作 – srcspider 2013-01-17 15:52:02
是的,我知道。这就是为什么我说它是'特定于平台的解决方案'。 – 2013-01-17 21:28:00
更多平台特定的解决方案:GNU/Linux平台提供'wget'。 OS X提供'curl'('curl http://oh.no/its/pbjellytime.flv --output secretlylove.flv')。 Windows有一个Powershell等效的'(新对象System.Net.WebClient).DownloadFile('http://oh.no/its/pbjellytime.flv','C:\tmp\secretlylove.flv')''。对于所有操作系统,wget和curl都可以通过下载进行二进制文件。我仍然强烈建议使用标准库,除非您的代码完全是为了您自己的喜好。 – fny 2013-01-23 12:51:34
例3在Ruby的net/http documentation展示了如何通过HTTP下载文件,并输出文件,而不是只加载到:它保存文件的一部分,同时下载解决方案内存,替代放入二进制写入文件,例如如Dejw的答案所示。
更复杂的情况在相同的文档中进一步显示。
扩展在Dejw的答案(EDIT 2):
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使用率。 Net :: HTTP不会等待缓冲区(v1.9.2中的16kB)在生成之前填充,因此CPU忙于移动小块。沉睡片刻让缓冲区有机会在写入之间填充,CPU使用率与curl解决方案相当,在我的应用程序中有4-5倍的差异。一个更强大的解决方案可能会检查f.pos
的进度并将超时调整为目标,例如缓冲区大小的95% - 事实上,在我的示例中,这就是我得到的0.005数字。
对不起,但我不知道更优雅的方式让Ruby等待缓冲区填充。
编辑:
这是自动调整,以保持缓冲在低于或等于容量的版本。这是一个不雅的解决方案,但它看起来速度一样快,并且使用尽可能少的CPU时间,因为它正在调用curl。
它分三个阶段工作。有意识的长时间睡眠时间的简短学习时间确定了完整缓冲区的大小。丢弃期通过每次迭代快速减少睡眠时间,将其乘以更大的因子,直到找到欠填充的缓冲区。然后,在正常时期,它会上下调整一个较小的因子。
我的Ruby有点生锈,所以我相信这可以改进。首先,没有错误处理。此外,也许它可以分离成一个对象,远离下载本身,所以你只需要在你的循环中调用autosleep.sleep(f.pos)
?更妙的是,网:: 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
我喜欢'睡眠'黑客! – Radek 2011-08-06 04:02:12
我有问题,如果该文件包含德国的变音之前(ä,ö,ü)。我可以通过使用解决的问题:
ec = Encoding::Converter.new('iso-8859-1', 'utf-8')
...
f << ec.convert(seg)
...
我知道这是一个老问题,但谷歌把我在这里,我想我找到了一个简单的答案。
在Railscasts #179,瑞恩·贝茨使用Ruby的标准类OpenURI做很多东西,有人问这样的:
(警告:未经测试的代码,您可能需要更改/调整它。)
require 'open-uri'
File.open("/my/local/path/sample.flv", "wb") do |saved_file|
# the following "open" is provided by open-uri
open("http://somedomain.net/flv/sample/sample.flv", "rb") do |read_file|
saved_file.write(read_file.read)
end
end
还有比Net::HTTP
更多的API友好的库,例如httparty:
require "httparty"
File.open("/tmp/my_file.flv", "wb") do |f|
f.write HTTParty.get("http://somedomain.net/flv/sample/sample.flv").parsed_response
end
您可以使用开放式的URI,这是一个内衬
require 'open-uri'
content = open('http://example.com').read
或通过网/ HTTP
require 'net/http'
File.write("file_name", Net::HTTP.get(URI.parse("http://url.com")))
这里是我的Ruby HTTP到文件使用IO::copy_stream(src, dst)
。
require "open-uri"
def download(url, path)
File.open(path, "w") do |f|
IO.copy_stream(open(url), f)
end
end
这里的主要优点是它读取和写入块,因此不会读取内存中的整个响应。
我使用open(name, *rest, &block)
作为本演示的目的。 IO::copy_stream(src, dst)
的第一个参数可以是任何响应读取的IO对象。
请注意用户提供的输入! open(name, *rest, &block)
是不安全的,如果name
来自用户输入!
这应该是公认的答案,因为它简洁明了,并且不会加载整个文件在内存中〜+性能(在这里猜测)。 – Nikkolasg 2016-09-12 13:56:41
我同意Nikkolasg。我只是试图使用它,它工作得很好。我修改了一下,例如,本地路径将从给定的URL自动推断出来,所以e。 G。 “path = nil”然后检查是否为零;如果它是零,那么我使用URL上的File.basename()来推断本地路径。 – shevy 2017-07-03 11:41:12
我不知道为什么它可以正确使用''w“'。它会在Windows上工作还是更好地使用''wb“'而不是? – sekrett 2017-12-04 10:59:11
,如果你正在寻找一种方式如何下载的临时文件,做的东西,并删除它试试这个宝石https://github.com/equivalent/pull_tempfile
require 'pull_tempfile'
PullTempfile.transaction(url: 'https://mycompany.org/stupid-csv-report.csv', original_filename: 'dont-care.csv') do |tmp_file|
CSV.foreach(tmp_file.path) do |row|
# ....
end
end
我的解决方案是强烈基于http://snippets.dzone.com/posts/show/2469在FireFox地址栏中输入__ruby文件download__之后出现...在你问这个问题之前,你是否在互联网上做过任何研究? – 2010-02-15 01:17:15
@Dejw:我做了研究,在这里找到了一个回答的问题。基本上用你给我的相同的代码。 'resp.body'部分令我困惑,我认为它只会保存响应的'body'部分,但我想保存整个/二进制文件。我还发现http://rio.rubyforge.org/可能会有所帮助。此外,我的问题没有人可以说这样的问题还没有回答:-) – Radek 2010-02-15 01:23:28
正文部分正好是整个文件。响应是从标题(http)和正文(文件)创建的,所以当您保存正文时您保存了该文件;-) – 2010-02-15 01:54:57