如何通过HTTP下载二进制文件?

如何使用Ruby下载和保存二进制文件通过HTTP?

该URL是http://somedomain.net/flv/sample/sample.flv

我在Windows平台上,我不想运行任何外部程序。

最简单的方法是平台特定的解决scheme:

  #!/usr/bin/env ruby `wget http://somedomain.net/flv/sample/sample.flv` 

可能你正在寻找:

 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:下载时保存文件的一部分的解决scheme:

 # 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 

我知道这是一个古老的问题,但Google把我扔在这里,我想我find了一个更简单的答案。

在Railscasts#179中 ,Ryan Bates使用Ruby标准类OpenURI做了很多这样的问题:

警告 :未经testing的代码,您可能需要更改/调整它。)

 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 

Ruby的net / http文档中的示例3显示了如何通过HTTP下载文档,并输出该文件,而不是将其加载到内存中,用二进制写入文件replaceput,例如Dejw的答案中所示。

更复杂的情况在相同的文件中进一步显示。

你可以使用open-uri,这是一个class轮

 require 'open-uri' content = open('http://example.com').read 

或者使用net / 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对象。

请注意用户提供的input! 如果name来自用户inputopen(name, *rest, &block)是不安全的!

扩展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 } } } } 

其中filenameurl是string。

当networking是限制因素时, sleep命令是一种可以显着降低CPU使用率的黑客攻击。 Net :: HTTP并不等待缓冲区(v1.9.2中的16kB)在yielding之前填充,所以CPU忙于移动小块。 沉睡了一会儿,缓冲区就有机会在写入之间填充,CPU使用率与curl解决scheme相当,在我的应用程序中有4-5倍的差异。 一个更强大的解决scheme可能会检查f.pos进度, f.pos超时调整为目标,例如95%的缓冲区大小 – 事实上,我的例子是这样得到了0.005的数字。

对不起,但我不知道让Ruby等待缓冲区填充的更优雅的方式。

编辑:

这是一个自动调整自己的版本,以保持缓冲区在或低于容量。 这是一个不雅的解决scheme,但它似乎是一样快,并使用尽可能less的CPU时间,因为它呼吁curl。

它分三个阶段工作。 一个短暂的学习时间,故意长时间的睡眠时间,build立一个完整的缓冲区的大小。 在每次迭代中,下降周期通过将其乘以更大的因子来快速减less睡眠时间,直到find欠填充的缓冲区。 然后,在正常时期,上下调整一个较小的因子。

我的ruby有点生锈,所以我相信这可以改进。 首先,没有error handling。 此外,也许它可以分离成一个对象,远离下载本身,所以你只需要在你的循环中调用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 

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 

我有问题,如果文件包含德国变音(ä,ö,ü)。 我可以用下面的方法解决这个问题:

 ec = Encoding::Converter.new('iso-8859-1', 'utf-8') ... f << ec.convert(seg) ... 

如果你正在寻找一个方法如何下载临时文件,做的东西,并删除它试试这个gemhttps://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