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