logo
  • #Ruby
  • #Ruby on Rails

Rubyで大容量ファイルをストリーム処理する

post-cover

ファイルをストリームで読み込んで処理をする方法について勉強した内容を整理しました。

ストリーム処理とは?

ストリーム処理とは、ファイルをダウンロードしつつ、取得したストリーミングデータに対して何らか処理することです。

イメージとしては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で改行ごとに取得してしまう状態なので、この辺りはもっとうまくコントロールする方法があるはずですね...引き続き調査したいと思います。

© 2025 Koji Dev Blog