JavaScript Base64 实例:JSON、文件和字节数组

用支持 UTF-8 的 JavaScript 处理 JSON 与本地文件,避免把任意二进制误当成文字。

写 Base64 代码之前先确定输入是什么:JavaScript 字符串需要选择字符编码;文件本来就是字节;JSON 对象则需要先序列化。把步骤分开,能避免重复编码,也方便定位失败发生在哪一层。

编码小型 JSON 对象

对象先经过一次 JSON.stringify,再转为 UTF-8 字节,最后编码 Base64。示例保留中文字段,可以发现只对英文有效的错误实现。临时字节字符串会占用额外内存,因此此写法面向小型数据。

const value = { message: '你好', count: 3 };
let binary = '';
for (const byte of new TextEncoder().encode(JSON.stringify(value))) {
  binary += String.fromCharCode(byte);
}
const encoded = btoa(binary);
const bytes = Uint8Array.from(atob(encoded), c => c.charCodeAt(0));
const json = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
console.log(JSON.parse(json).message); // 你好

Base64 解码成功不代表 JSON 有效。UTF-8 解码和 JSON.parse 应分别处理错误。已经是 JSON 文本的字符串如果再次序列化,会增加引号和转义字符,从而改变数据。

文件按原始字节读取

浏览器中的 File 可以通过 arrayBuffer() 读取。图片、压缩包或加密文件不能通过 file.text() 再编码,否则无法作为字符解释的字节可能被替换。目标需要 Data URI 时,可以使用 FileReader.readAsDataURL() 获得前缀和数据。

const bytes = new Uint8Array(await file.arrayBuffer());
console.log(bytes.byteLength); // 原始文件的字节长度

用往返测试检查结果

比较还原后的字节和原字节,不只比较文件名或截图。测试应包含空输入、0 到 255 的全部字节、中文、emoji,以及末组剩一或两个字节的数据。批量处理时每个文件都要保留独立状态,避免失败结果覆盖成功文件。

除非明确调用 API,否则转换可以留在本地。把 Base64 发送到网络仍然等于发送可还原的原始数据。浏览器方法的行为见 MDN: btoa()。