浏览器与 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()。