ブラウザと Node.js の Base64:共通のバイト処理規約と 2 つの API

ブラウザーでは TextEncoder を、Node.js では Buffer を使用し、同じ Unicode およびバイナリ フィクスチャに対して出力をテストします。

ブラウザ クライアントと Node.js サービスは、同じバイトに対して同じ Base64 を生成する必要があります。これを行うために同じ API を使用する必要はありません。重要な合意は、テキストのバイト表現、アルファベット、パディングと無効な入力の処理です。

ブラウザ: UTF-8 ステップを明示的にします。

従来のブラウザ機能はバイト文字列を操作します。 Unicode テキストを変換する TextEncoder、それらのバイトをエンコードします。リターン パスで、バイト配列を再構築し、UTF-8 を明示的にデコードします。この例ではループを使用して、大きな配列が関数の引数に拡張されるのを避けていますが、実稼働ファイル エンコーダーでは引き続き、境界のあるチャンクまたは適切なバイト API を使用する必要があります。

const text = '中文😊';
const bytes = new TextEncoder().encode(text);
let binary = '';
for (const byte of bytes) binary += String.fromCharCode(byte);
const encoded = btoa(binary);
console.log(encoded); // 5Lit5paH8J+Yig==
const decodedBytes = Uint8Array.from(atob(encoded), c => c.charCodeAt(0));
const decoded = new TextDecoder('utf-8', { fatal: true }).decode(decodedBytes);
console.log(decoded); // 中文😊

Node.js: Buffer をバイト境界に保持します

Buffer.from(text, 'utf8') テキストエンコーディングを表示します。電話をかける toString('base64') 次に、それらのバイトを表します。 Buffer にデコードすると、バイナリ データが保持されます。 UTF-8 文字列への即時変換は、ペイロードがテキストであることがわかっている場合にのみ適切です。

import { Buffer } from 'node:buffer';
const encoded = Buffer.from('中文😊', 'utf8').toString('base64');
console.log(encoded); // 5Lit5paH8J+Yig==
const bytes = Buffer.from(encoded, 'base64');
console.log(new TextDecoder('utf-8', { fatal: true }).decode(bytes)); // 中文😊

この例の致命的な TextDecoder は意図的なものです。通常の Buffer UTF-8 文字列変換では、無効な入力に対して置換文字を挿入できます。テキスト表示が成功すると、元のバイトが忠実に表現できないことが隠蔽されます。

検証については別途同意する

ノードの Base64 デコーダは、より厳格なアプリケーションが拒否する可能性のある一部の入力フォームを受け入れます。成功した Buffer 変換を、プロトコルの正規表現が提供されたことの証明として使用しないでください。署名、キャッシュキー、相互運用可能なシリアル化に関して正確な構文が重要な場合は、デコードする前に構文を確認してください。

共有回帰テストの場合は、ASCII、中国語、絵文字、ゼロ バイト、バイト FF、および 3 バイト境界の周囲のすべての長さを含めます。ランタイム間でバイトを比較します。また、実際に置換文字を含むフィクスチャを使用して、URL セーフな出力をテストします。出力が文字のみである入力は、バリアント オプションが機能することを証明できません。

Node でファイルを読み取るときは、読み取ったファイルにテキスト エンコーディングを適用するのではなく、返された Buffer を保持します。ブラウザで使用します File.arrayBuffer()。これらの小さな境界を選択することで、ほとんどの偶発的な破損を回避できます。参照 Node.js Buffer ノードの動作と MDN: btoa() ブラウザのバイト文字列規約の場合。