【问题标题】:Using rest-client to download a file to disk without loading it all in memory first使用 rest-client 将文件下载到磁盘而不首先将其全部加载到内存中
【发布时间】:2015-05-14 15:20:10
【问题描述】:

我正在使用 rest-client 下载大页面(大小约为 1.5 GB)。检索到的值存储在内存中,而不是保存到文件中。结果我的程序因failed to allocate memory (NoMemoryError) 而崩溃。

但是没有必要将这些数据保存在内存中,甚至可以直接保存到磁盘中。

我发现“您可以:(...) 手动处理响应(例如,将其作为流进行操作,而不是将其全部读入内存)有关更多信息,请参阅 RestClient::Request 的文档。”在https://github.com/rest-client/rest-client 不幸的是,在阅读了http://www.rubydoc.info/gems/rest-client/1.7.3/RestClient/Request 之后,我不知道它是如何实现的。

我也知道我可能会使用其他库 (Using WWW:Mechanize to download a file to disk without loading it all in memory first),但我的程序已经在使用 rest-client。

简化代码:

data = RestClient::Request.execute(:method => :get, :url => url, :timeout => 3600)
file = File.new(filename, 'w')
file.write data
file.close

代码 - https://github.com/mkoniecz/CartoCSSHelper/blob/395deab626209bcdafd675c2d8e08d0e3dd0c7f9/downloader.rb#L126

【问题讨论】:

  • rubydoc.info/gems/rest-client/1.7.3/RestClient/… 读取这个方法的源码并像这样实现,但是直接将数据存储在文件中。
  • 您可以使用内置库 OpenURI:require 'open-uri'; File.open(path, 'w') { |f| IO.copy_stream(open(url), f) }。如果文件很大,open 会自动将其写入 Tempfile 并返回。如果文件足够小,它会将其写入StringIO。无论哪种方式,您都将拥有一个 io 对象,您可以将其块复制到所需位置。
  • 你用 RestClient 解决了这个问题吗?我有类似的问题,但不能使用 open-uri,因为它不支持 POST 请求。

标签: ruby memory memory-management download out-of-memory


【解决方案1】:

另一种方法是使用raw_response。这会直接保存到文件中,通常在/tmp 中,并且可以毫无问题地处理重定向。 见Streaming Responses。这是他们的例子:

>> raw = RestClient::Request.execute(
           method: :get,
           url: 'http://releases.ubuntu.com/16.04.2/ubuntu-16.04.2-desktop-amd64.iso',
           raw_response: true)
=> <RestClient::RawResponse @code=200, @file=#<Tempfile:/tmp/rest-client.20170522-5346-1pptjm1>, @request=<RestClient::Request @method="get", @url="http://releases.ubuntu.com/16.04.2/ubuntu-16.04.2-desktop-amd64.iso">>
>> raw.file.size
=> 1554186240
>> raw.file.path
=> "/tmp/rest-client.20170522-5346-1pptjm1"

【讨论】:

    【解决方案2】:

    我的原始答案提倡将块传递给RestClient::Request#execute,但这仅在读取完整响应后才将数据传递给块。从而使练习变得毫无价值。这是正确的做法:

    File.open('/tmp/foo.iso', 'w') {|f|
        block = proc { |response|
          response.read_body do |chunk|
            puts "Working on response" 
            f.write chunk
          end
        }
        RestClient::Request.new(method: :get, url: 'http://mirror.pnl.gov/releases/xenial/ubuntu-16.04-server-amd64.iso', block_response: block).execute
    }
    

    来自相关rest-client project issue

    注意:重定向在此模式下也不起作用,您会丢失 HTTP 退出状态、cookie、标头等。希望有一天能解决这个问题。

    【讨论】:

      猜你喜欢
      • 2011-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-11
      • 2013-01-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多