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