瀏覽器與 Node.js 的 Base64:同一位元組契約,兩套介面

瀏覽器使用 TextEncoder,Node.js 使用 Buffer,再用相同 Unicode 與二進位制樣本驗證一致性。

瀏覽器客戶端與 Node.js 服務對同一組位元組,應產生相同 Base64。它們不必使用同一 API,但必須約定文本如何變成位元組、使用哪種字母表,以及如何處理填充和非法輸入。

瀏覽器:明確 UTF-8 步驟

傳統瀏覽器方法處理位元組字串。先使用 TextEncoder 轉為 UTF-8,再編碼;解碼後還原位元組陣列,再解釋為 UTF-8。這裡用迴圈避免一次展開所有函式引數,但大型檔案仍應使用分塊或合適的位元組介面。

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 可保留二進位制;只有確定載荷是文字時,才應立即轉成字串。

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 轉換可能用替換字元代替非法輸入,讓顯示成功掩蓋位元組丟失。

校驗規則需要另行約定

Node 解碼器接受的某些輸入,不一定符合應用協議的規範形式。簽名、快取鍵或互操作序列化依賴準確語法時,應先校驗再解碼,不能把 Buffer 轉換成功當作規範證明。

跨執行時測試應包含 ASCII、中文、emoji、零位元組、FF,以及三位元組邊界附近的長度。URL-safe 測試還要選擇確實產生替換字元的樣本,否則全字母輸出無法證明選項有效。Node 讀檔案時保留 Buffer,瀏覽器使用 File.arrayBuffer()。介面行為見 Node.js Buffer 和 MDN: btoa()。