BigQueryロードする前の文字コード変換

2021/06/30に公開されました。
2021/11/15に更新されました。

BigQueryにロードするために、UTF-8でないCSVファイルの文字コード変換を語る


author: kyou

背景

Cloud Storage(GCS)からCSVデータをBigQueryに読み込む際は、様々な注意点がありますが、文字コードはそのひとつです。 公式ドキュメントには、下記の文言が含まれています。

BigQuery に読み込む CSV データは UTF-8 でエンコードされている必要があります。 ISO-8859-1(Latin-1)の場合は--encodingで明記する必要はあります。 Cloud Storage からの CSV データの読み込み

ということで、Shift_JISやEUC-JPなどが多用されているため、BigQueryにロードする前に、UTF-8へ変換しなければなりません。

ここではiconvコマンドを用いて、Shift_JIS(CP932)をUTF-8へ変換をCloud Functions、Cloud Runで試みます。

Shift_JIS のバリエーションについて、Shift_JIS 系文字一覧イメージと SJIS・MS932・CP943・SJIS2004 の違いを参照してください。

Cloud Functions(GCF)

GCSにアップロードしたファイルを処理するといったら、最初に浮かべるのはGCFでしょう。

GCF で叩けるシェルコマンド一覧の記事に書いてありますが、2021/06/27時点でもiconvコマンドはGCF環境で利用できます。

GCSのバケットの作成とGCF(トリガー: google.storage.object.finalize)のデプロイについて、省略させていただき、処理流れ(GCSからダウンロード、文字コード変換とGCSにアップロード)を簡単に説明します。

GCS からダウンロード

まず、CSVファイルをGCF実行環境の/tmpにダウンロードします。

ファイルシステムで書き込み可能な部分は/tmpディレクトリだけです。 GCF 実行環境: ファイル システム

ダウンロード処理のソースコードはGoogle Cloud Storage: Node.js Client Doc download exampleを参照してください。

文字コード変換

/tmpにダウンロードしたCSVファイルをiconvコマンドで文字コード変換が行います。 変換済みファイルも/tmpに格納します。

const execSync = require("child_process").execSync;
const newFileName =
  path.parse(file.name).name + "_utf8" + path.parse(file.name).ext;
// iconvコマンドを実行する
execSync(
  `iconv -f cp932 -t utf8 ${path.join(os.tmpdir(), file.name)} -o ${path.join(
    os.tmpdir(),
    newFileName,
  )}`,
);
console.log(`${path.join(os.tmpdir(), newFileName)} has been convert to UTF8.`);

GCS にアップロード

文字コード変換済みのCSVファイルはGCSにアップロードします。 アップロード処理のソースコードはGoogle Cloud Storage: Node.js Client Doc upload exampleを参照してください。

注意点

アップロード

Google Cloud Storage: Node.js Clientはアップロードするファイルサイズが5 MBより大きい場合は、options.resumabletrueに設定されると、下記のエラーとなります。

A resumable upload could not be performed. The directory, /root/.config, is not writable. You may try another upload, this time setting options.resumable to false.

options.resumablefalseにすれば解決します。

また、トリガーでループさせないように、変換後のファイルは違うGCSバケットにアップロードしましょう。

ファイルシステム

GCF 実行環境: ファイル システムの記述にもありますが、/tmpはメモリに格納される「tmpfs」ボリュームであるため、GCFに消費するメモリリソースを注意する必要があります。デフォルト256 MBでデプロイする場合は、117 MBファイルのダウンロードにmemory limit exceededのエラーになります。

GCF ベストプラクティス: 一時ファイルを常に削除するによりますと、GCF実行後にも消費したメモリは維持される場合があるため、アップロード処理が終わったら、GCF実行環境のファイルを削除しましょう。

Cloud Run

今まで主にHTTPSとPub/SubがトリガーだったCloud Runは、Eventarc の GAにより、イベント駆動型サービスとして動作するようになりました。つまり、Cloud StorageにCSVファイルをアップロードすることがトリガーとなり、上記GCFと同じ処理ができるはずです。

2021 年 11 月 15 日に追記: Eventarc の新しい Cloud Storage トリガーの概要の公式ブログにより、これからはGCSの監査ログを経由しなくてもできます。

Codelab: Eventarc から受信したイベントを使って Cloud Run をトリガーするでは、実現方法をまとめています。文字コード変換処理のため、変更箇所と注意点はここに記述します。

ソースコード

公式ドキュメントのサンプルリポジトリのソースに下記の変更を加えます。そして、GCFで実装した部分はも流用できます。

// package.jsonにも@google-cloud/storageを追記
const { Storage } = require("@google-cloud/storage");

// 中略

const relativePath = req.header("ce-subject");

console.log(`Detected change in Cloud Storage bucket: ${relativePath}`);

const parts = relativePath.split("/");
// objects/random.txt
const fileName = parts[parts.length - 1];

// GCFでの実装したファイル処理も省略

ファイルシステム

Cloud Runもインメモリファイルシステムのため、捌けるファイルのサイズはデプロイ時に設定したメモリと関係あります。そして、アップロードしたファイルも削除しましょう。

まとめ

文字コード変換処理においては、Cloud RunよりCloud Functionsのほうが適切ですね。 Eventarcの登場によって、Cloud RunはCloud Storageと「直接」につながることができていますが、利用するGCPサービスも増えました。 プロダクション環境で使うには、権限も料金も工夫する必要があります。

※本記事は、ジーアイクラウド株式会社の見解を述べたものであり、必要な調査・検討は行っているものの必ずしもその正確性や真実性を保証するものではありません。

※リンクを利用する際には、必ず出典がGIC dryaki-blogであることを明記してください。
リンクの利用によりトラブルが発生した場合、リンクを設置した方ご自身の責任で対応してください。
ジーアイクラウド株式会社はユーザーによるリンクの利用につき、如何なる責任を負うものではありません。