🎉

firebaseのfunctionにてCSVファイル(SJIS)からUNICODE変換で詰まりに詰まった話

2023/09/12に公開

CSVファイル(SJIS)からUNICODE変換で詰まりに詰まったので

業務にてfirebaseのstorageに保存してあるCSVファイル(文字コードSJIS)を
cloud functionから読込んでUNICODEに変換する際にドハマりしたので忘れない為にここに残します。

経緯

CSVから取り出した値をfirestoreに新規ドキュメントとして保存したいが為に作成しました。
ただ、今回のタイトルの様に文字化けしたまま登録は出来ないので対処が必要とのことで着手が始まりました。

createReadStreamを使用

下記の様にファイルを指定して、createReadStreamを使用して徐々に読み込む方式にしました。
CSVファイルが大量のデータの場合、システムに影響あるとおもうのでこちらの対応しています。

encodeには下記パッケージを利用しています。
利用方法はリンク先で確認してください。
https://github.com/polygonplanet/encoding.js/blob/master/README_ja.md#unicode-について

// bucketNameにはFirebaseコンソールから取得したバケット名
// fileNameにはstorageにあるfile名
const bucket = storage.bucket(bucketName)
const file = bucket.file(fileName) 

file.createReadStream()
        .on('error', (error) =>{ console.error(error) })
        .on('data', (chunk) => {
          const codes = new Uint8Array(chunk) 
          const encoding = Encoding.detect(codes) 
          console.log(encoding + ' is sjis')
          const sjisArray = Array.from(codes) 
	  // エンコード
          const utf8Array = Encoding.convert(sjisArray, {
            to: 'UTF8', // to_encoding
            from: 'SJIS', // from_encoding
          })
	  
	  const utf8Str = Buffer.from(utf8Array).toString('utf8').trim() 
          const data = utf8Str.split('\n')
... 以下略

エンコードまではよかったのですが、dataの値が切れる現象が発生。
(data[0]しかないのに、値が切れるのでdata[0],data[1]と値が増える現象発生)

調べると、chunkにはデータがある一定量をこえると分割することがあるとのこと。

on('data')ではなく、on('end')で処理

下記コードに変更しました。
chunkListにchunkを入れてますが、ただListの中はデータが途切れ途切れの状態です。
なので、on('end')で下記を使ってデータを繋げています。

const binaryData = Buffer.concat(chunkList)
  file.createReadStream()
        .on('error', (error) =>{ console.error(error) })
        .on('data', (chunk) => {
          chunkList.push(chunk)
        })
        .on('end', () => {
          if (chunkList.length === 0) {
            console.log('No data')
            return
          }

          const binaryData = Buffer.concat(chunkList)
          const sjisArray = Array.from(binaryData) 
	  // UNICODEに変更
          const utf8Array = Encoding.convert(sjisArray, {
            to: 'UNICODE', 
            from: 'SJIS', 
          })
          
          const utf8Str = Encoding.codeToString(utf8Array)
          const data = utf8Str.split('\n')
	  
....以下略
       

他の方法で試した際の注意点

const fs = require('fs');

調べるとよく見ると思います。
下記に様に使用するのですが、fileを直接読み込まないといけないらしく、Firebaseコンソールから取得したファイルだとfunction内に一度ファイルをダウンロードしてそれから処理をしなければならないとのこと。
ファイルをダウンロードするのにデータ容量の問題が怖かったので没。

fs.createReadStream('ここにファイル名')

reader = new FileReader();

余り使ったことないのであれですが、画像のupなどで使用することが多い印象ですが、
こちらはNode.jsのサーバーサイドでは使用ができませんでした。
エラーとして定義されていないと言われちゃいました。
あくまでWebブラウザ側での機能の様です。

総括

文字コード変換にこれほど苦労させられるとは思いませんでした。
CSVの文字コードが統一されているならそれでいいのですが、もしSJISからUNICODE変換しなければならない様なら参考にしてみてください。

Discussion