使用rest-client将文件下载到磁盘,而不是先将其全部加载到内存中

时间:2015-03-12 12:51:20

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

我使用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

2 个答案:

答案 0 :(得分:5)

另一种方法是使用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"

答案 1 :(得分: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,标题等。希望有一天它会被修复。