最近の案件で、AWS S3に配置された1GB程度のCSVファイルを処理することがありました。 そのままメモリに載せてしまうとすぐにメモリオーバーになってしまうので、ストリーム処理で実装することにしました。 その時の内容を整理してみましたので、同様のことで悩んでいる方の参考になれば嬉しいです。
前提
S3クライアントとして「aws-sdk-s3」を使用します。
ローカルにS3のファイルダウンロードする
まずは、S3にあるファイルをローカルにダウンロードします。
S3から直接ストリーミングしながら処理をしていくことも可能なのですが、途中でネットワーク等に異常があった場合のリカバリを考慮する必要があるので、ローカルにダウンロードしてから処理することをオススメします。
実装
事前にS3のクライアントをインスタンス変数で作成しておきます。
Aws.config.update( region: 'ap-northeast-1', credentials: Aws::Credentials.new(ENV['S3_BUDGET_ACCESS_KEY'], ENV['S3_BUDGET_SECRET_ACCESS_KEY']) ) @bucket = ENV['S3_BUDGET_NAME'] @s3_client = Aws::S3::Client.new
ローカル上の「tmp/temp_file.csv(任意の名前)」というファイルに、S3上の@bucket内にある「s3_file.csv(任意の名前)」の内容をコピーします。
コピー時に大量のデータ転送が発生しますので、ある程度の時間を要します。
File.open('tmp/temp_file.csv', 'wb') do |file| @s3_client.get_object({ bucket: @bucket, key:'s3_file.csv' }, target: file) end
ファイルをストリーム処理する
ローカル上のファイルをCSV.newでCSVファイルに変換して、eachで一行ずつ読み込みます。 こうすることでストリーム処理でファイルを読み込むことができます。
メモリに乗るのは一行ごとのデータになりますので、メモリに優しく処理をすることができます。
File.open('tmp/temp_file.csv', 'r') do |file| CSV.new(file).each do |row| # ここに処理を書いていきます end end
参考:
Downloading Objects from Amazon S3 using the AWS SDK for Ruby | AWS Developer Tools Blog