JavaScriptのTextEncoderを使ってJSONのバイトサイズを調べる
AppSyncでとってきたGraphQLのレスポンスのサイズを調べたくなり、JavascriptにTextEncorderというものがあることを知ったので、備忘もかねてご紹介します!
なぜバイトサイズを測りたくなったか
GraphQL + DynamoDB を使っていると、AppSyncやDynamoDBのレスポンスサイズ制限(1MB)に引っかかることがあります。
今回は、200件程度あるはずのデータが100件しか取れていなかったため、レスポンスのサイズを図ろうとしていました。
しかし、単に data.length
とか JSON.stringify(data).length
で文字数やアイテム数を見ても、正確なバイト数ではありません。
実際に "あ"
のような日本語の1文字は、UTF-8では3バイト使います。
でも .length
は1を返します。これでは、制限にかかるかどうかの判断に使えません。
最初に試した方法とその落とし穴
まずは単純に JSON.stringify(...).length
を使ってみました。
const str = JSON.stringify(data);
console.log(str.length); // ← これは文字数
ですが、これは 「文字数」 であり、「バイトサイズ」ではありません。
UTF-8のような可変長文字コードでは、1文字 = 1バイトとは限りません。
TextEncoderにたどり着いた
いろいろ調べてたどり着いたのが、TextEncoder というAPIでした。
これは、文字列をUTF-8でエンコードして、Uint8Array(バイナリ配列)を返してくれる便利なWeb APIです。
const encoder = new TextEncoder();
const json = JSON.stringify(data);
const bytes = encoder.encode(json);
console.log(bytes.length); // ← 正確なバイトサイズ!
これで「見た目は小さく見えるけど、実際は大きかった」データも正確に測れます。
実際のコード例:大量データのサイズを測ってみる
例えば、AppSyncでページネーションしたいときに、「1ページ何件まで大丈夫?」と確認するためのコードがこちらです。
const encoder = new TextEncoder();
const sizes = items.map(i => encoder.encode(JSON.stringify(i)).length);
const total = sizes.reduce((a, b) => a + b, 0);
const avg = total / sizes.length;
console.log({
件数: sizes.length,
平均サイズ: avg,
合計サイズ: total,
最大サイズ: Math.max(...sizes),
});
これで、トータルのレスポンスサイズと1アイテムの中での最大サイズが分かります。
ほかにも使える場面
TextEncoderは、他にもこんな場面で使えるのではないかと思います。
- LocalStorage の容量上限(約5MB)に収まるかチェックしたいとき
- メール本文の長さをバイトで見積もりたいとき
- CSVファイル出力時のサイズ確認
- ネットワーク送信前にサイズを調べて分割処理する場合 など
注意点
- UTF-8専用:TextEncoder は UTF-8 固定。他の文字コード(Shift_JISなど)は扱えません。
- Node.jsでも使用可能:v11以降であれば、global.TextEncoder として利用できます。
- 古いブラウザ(IEなど)では非対応ですが、モダン環境ならまず問題なしです。
まとめ
- JSON.stringify(...).length は文字数、正確なバイト数ではない
- バイトサイズを測りたいなら、TextEncoderが簡単で正確
- AppSyncやDynamoDB、LocalStorageなど、サイズ制限のあるAPIでの事前チェックにとても役立つ!
ぶっちゃけテキストファイルとして保存すればよかったか…?と思いましたが、覚えておいて損はないAPIかと思います!
Discussion