LoginSignup
7
2

More than 3 years have passed since last update.

Tips: Rubyでresponse bodyをstreamで受け取ってNoMemoryErrorを回避する

Posted at

RubyやRailsなどでHTTPリクエストを行う場合に、response bodyをstreamで受け取る方法を紹介します。
単純にリクエストをするとレスポンスの結果を全てメモリに載せてしまうので、メモリの節約や巨大なデータを取得するとNoMemoryErrorが発生してしまいます。
今回の手法はこのエラーになってしまう状態を回避する方法として有効です。

Net::HTTPの場合

にある通り、read_bodyというメソッドを用いることでstreamでデータの受け取りを行うことができます。ファイルに書き出すことでメモリを圧迫せずにデータを取得できます。

require 'net/http'

uri = URI.parse('http://example.com/')
Net::HTTP.start(uri.host, uri.port) do |http|
  http.request_get(uri.path) do |response|
    File.open("file_path", "w") do |file|
      response.read_body do |chunk|
        file.write(chunk)
      end
    end
  end
end

また、streamで取得したデータの累計サイズを記録しておくことで、特定のサイズでデータの取得を打ち切るようなこともできます。

require 'net/http'

uri = URI.parse('http://example.com/')
response_body = ''
total_size = 0
Net::HTTP.start(uri.host, uri.port) do |http|
  http.request_get(uri.path) do |response|
    response.read_body do |chunk|
      response_body += chunk if total_size <= 1000000 # 1Mバイトまでのデータまで受け取る
      total_size += chunk.bytesize
    end
  end
end

Faradayの場合

にある通り、on_dataというオプションにprocを渡すことでread_bodyと同じようなことができます(このオプションが使えるのはNet::HTTPをアダプタにしている場合のみです)

require 'faraday'

connection = Faraday.new(url: 'http://example.com/')
response_body = ''
connection.get('/') do |request|
  request.options.on_data = proc do |chunk, overall_received_bytes|
    puts "Received #{overall_received_bytes} characters"
    response_body += chunk
  end
end

また、Net::HTTPと同じようにファイルに書き出すこともできますし、切り捨ても行えます。

require 'faraday'

connection = Faraday.new(url: 'http://example.com/')
connection.get('/') do |request|
  request.options.on_data = proc do |chunk, overall_received_bytes|
    puts "Received #{overall_received_bytes} characters"
    File.open("file_path", "w") do |file|
      file.write(chunk)
    end
  end
end
require 'faraday'

connection = Faraday.new(url: 'http://example.com/')
response_body = ''

connection.get('/') do |request|
  request.options.on_data = proc do |chunk, overall_received_bytes|
    puts "Received #{overall_received_bytes} characters"
    response_body += chunk if overall_received_bytes <= 1000000 # 1Mバイトまでのデータまで受け取る
  end
end
7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2