Rubyでxlsxファイルを読めるrooというgemでエクセルを読んでいる箇所がエラーになるようになりました。
しかもローカル環境のMacでのみ発生し、AWSのAmazonLinuxやUbuntu上では問題なく動くという罠っぷり。これのおかげでだいぶハマりました。
問題のはS3上に置いたエクセルをRubyで開こうとしたときで、コードはだいたいこんな感じでした。
1client = Aws::S3::Client.new(
2 region: region,
3 access_key_id: access_key,
4 secret_access_key: secret_access_ke,
5)
6
7file = client.get_object(bucket: bucket_name, key: 'file.xlsx')
8spreadsheet = Roo::Excelx.new(file.body) # <= エラー発生
最後の行で
undefined method `bytesize' for nil:NilClass
のエラーが発生しました。
ここのfile.bodyはStringIOで、S3のファイルの中身を読むためのストリームで、少しずつ読み出す途中でエラーになっているようです。
rooやrooが内部で使っているrubyzipの中に潜ってデバッグしたけどよくわからず、issueを見てもすんなりいかず困ってたんですが、同僚に相談してたらそれっぽいissueを見つけてくれました。
このURLのaalvaradoさんのコメントに↓のような記載がありまして、
1Roo::Excelx.new(StringIO.new(xlsx_report.file_data))
NoMethodError: undefined method `bytesize’ for nil:NilClass
まさに今回踏んでいる現象のように見えました。この方はset_encodingでアスキー指定したら直ったということです。
1Roo::Excelx.new(StringIO.new(xlsx_report.file_data).set_encoding('ASCII-8BIT'))
さて、自身のコードに戻って、file.bodyのエンコーディングを調べてみるとUTF-8だということがわかりました。
StackOverflowに倣って
1file.body.set_encoding('ASCII-8BIT')
2spreadsheet = Roo::Excelx.new(file.body)
とやってあげたら見事にエラーがなくなりました。このコードはLinux上でも問題なく動いたのでそのまま採用としました。