ファイルをストリームで読み込んで処理をする方法について勉強した内容を整理しました。
ストリーム処理とは?
ストリーム処理とは、ファイルをダウンロードしつつ、取得したストリーミングデータに対して何らか処理することです。
イメージとしてはYoutubeなどがわかりやすいかと思います。 Youtubeは動画データを全てダウンロードしてから再生するのではなく、ダウンロードしながら動画も再生してくれますよね。
テキストファイルも同様で、ファイルをダウンロードしながら何らかの処理を並行で実行することで処理速度向上やUX改善につなげることができます。
やりたいこと
今回は国勢調査の人口統計データをHTTP経由でダウンロードしてみます。
RubyでHTTPを扱う場合にはnet/http
が採用されることが多いですが、これだと初回と2回目以降のダウンロードで挙動が異なることが確認できました(原因は調査中)。
したがって、同じくHTTPを扱うgemであるexconを使います。
gem install excon
まずは普通にダウンロードしてみる
exconでは↓のようにしてダウンロードできます。
require 'excon'
url = 'https://www.e-stat.go.jp/stat-search/file-download?statInfId=000031524010&fileKind=1'
connection = Excon.new(url)
response = connection.get
p response.body.bytesize
#=> 55038
ストリームで読み込む
参考:https://github.com/excon/excon#streaming-responses
ブロックを与えてあげることでchunk(ある大きさの塊)でファイルを分割してダウンロードすることができます。
require 'excon'
url = 'https://www.e-stat.go.jp/stat-search/file-download?statInfId=000031524010&fileKind=1'
connection = Excon.new(url)
connection.get(chunk_size: 1024) do |chunk|
p chunk.bytesize
end
#=> 1024
#=> 1024
#=> 1024
#=> 1024
#=> .
#=> .
#=> .
IO.pipeで繋ぐ
RubyのライブラリであるIO.pipeを使うことでIOオブジェクトを読み込みと書き込みに分離することができます。
これとRubyでマルチスレッドのプログラムを実現するThreadクラスを組み合わせることで、ファイルをダウンロードしながら並行でそれを読み出すことができます。
require 'excon'
url = 'https://www.e-stat.go.jp/stat-search/file-download?statInfId=000031524010&fileKind=1'
connection = Excon.new(url)
IO.pipe do |read_io, write_io|
t = Thread.start do
connection.get(chunk_size: 10000) do |chunk|
write_io.puts(chunk)
end
rescue
write_io.close
ensure
t.kill
end
while chunk = read_io.gets
p chunk.bytesize
end
end
#=> 110
#=> 57
#=> 56
#=> 53
#=> .
#=> .
#=> .
read_io.gets
で改行ごとに取得してしまう状態なので、この辺りはもっとうまくコントロールする方法があるはずですね...引き続き調査したいと思います。