S3 bucketにあるファイルをダウンロードしてTempfileに書き込みしようとしたところ以下のエラーが発生しました。
Encoding::UndefinedConversionError: "\xE5" from ASCII-8BIT to UTF-8
その時のコードは以下の通りです
s3_client = Aws::S3::Client.new Tempfile.open(file_name) do |file| s3_client.get_object({ bucket: 'your-bucket', key: 'your-key' }, target: file) file end
ファイルはCSV形式でエンコーディングはUTF-8なのですが、ASCII-8BITとして見なされているようです。 原因と対処方法を整理したので、同様のことで悩んでいる方はぜひ参考にしてみてください。
エラーの原因
ASCII-8BITとはバイトストリームを取り扱う際に使用される特殊なエンコーディングです。S3上のファイルはエンコーディングがUTF-8のCSVファイルなのですが、S3からget_objectする際はバイナリデータとしてダウンロードされます。
バイナリデータをTempfileに焼き付けようとしたためEncoding::UndefinedConversionError
のエラーが発生したようです。
Tempfileは画像などのバイナリデータをIO.binmodeとしてバイナリモードにする必要があります。
対処方法
コードを下記に変更することで解決しました。
s3_client = Aws::S3::Client.new Tempfile.open(file_name) do |file| s3_client.get_object({ bucket: 'your-bucket', key: 'your-key' }, target: File.open(file, 'wb')) file end
バイナリ形式の書き込みモード(wb)でFile.openしてあげてからファイルを書き込むようにします。 こうすることでTempfileへのバイナリストリームでの書き込みが可能となります。
もう一つの方法として、以下のようにTempfileをbinmodeにしたものを使用して書き込む方法もあります。 今回はタイプ数の少ないFile.openの方を採用しましたが、こちらの対応のほうが一般的のようです。
temp = Tempfile.new('temp.csv') temp.binmode File.read(temp.path, encoding: "bom|utf-8")
参考
ruby - File encoding issue when downloading file from AWS S3 - Stack Overflow