很多开发者在第一次用 Base64 编码中文时都会遇到同一个问题:编码后再解码,中文变成了乱码。比如把"你好世界"编码后解码回来,得到的却是一堆 㽠好 之类的字符。
这不是 Base64 算法本身的 bug,而是字符编码在作怪。本文帮你彻底搞清楚 base64 中文乱码的根因,并给出正确的处理方式。
为什么 Base64 编码中文会乱码
Base64 本质上是一种二进制到文本的编码方案,它处理的是字节(bytes),不是字符。问题出在"字符 → 字节"这一步:
- UTF-8 中,一个中文字符占 3 个字节
- GBK 中,一个中文字符占 2 个字节
如果编码时用 UTF-8 把中文转成字节流,解码时却用 GBK 去还原,字节数对不上,自然就乱码了。
一个典型的错误场景:
// 错误示范:直接用 btoa 编码中文
btoa("你好世界");
// Uncaught DOMException: The string to be encoded contains characters outside of the Latin1 range.
浏览器的 btoa() 只支持 Latin1 字符(U+0000 到 U+00FF),遇到中文直接报错。有些人会用 escape() 绕过:
// 能跑,但有隐患
btoa(escape("你好世界"));
// 解码时必须配合 unescape(),否则就是乱码
atob(btoa(escape("你好世界"))); // 得到的是 escape 编码,不是原文
unescape(atob(btoa(escape("你好世界")))); // 这才能还原
这种方式虽然能工作,但 escape() 已被废弃,而且跨平台传输时如果对方不知道要 unescape,解码结果就是乱码。
正确的 Base64 中文编解码方法
核心原则:先把字符串按 UTF-8 转成字节数组,再做 Base64 编码;解码时反过来。
方法一:使用 TextEncoder / TextDecoder(推荐)
function base64EncodeUnicode(str) {
const bytes = new TextEncoder().encode(str); // 字符串 → UTF-8 字节
const binary = Array.from(bytes, b => String.fromCharCode(b)).join("");
return btoa(binary);
}
function base64DecodeUnicode(base64) {
const binary = atob(base64);
const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));
return new TextDecoder().decode(bytes); // UTF-8 字节 → 字符串
}
// 测试
const encoded = base64EncodeUnicode("你好世界,Hello!");
console.log(encoded); // "5L2g5aW95LiW55WM77yMSGVsbG8h"
const decoded = base64DecodeUnicode(encoded);
console.log(decoded); // "你好世界,Hello!"
方法二:Node.js 环境
// 编码
const encoded = Buffer.from("你好世界", "utf-8").toString("base64");
console.log(encoded); // "5L2g5aW95LiW55WM"
// 解码 —— 注意必须指定 utf-8
const decoded = Buffer.from(encoded, "base64").toString("utf-8");
console.log(decoded); // "你好世界"
// 乱码的根源:编码和解码用了不同的字符集
const wrongDecode = Buffer.from(encoded, "base64").toString("latin1");
console.log(wrongDecode); // "ä½ å¥½ä¸ç" ← 这就是你看到的乱码
方法三:处理已有的 GBK 编码数据
如果你的数据源是 GBK 编码(常见于老系统、Windows 中文环境),需要先转成 UTF-8:
// 浏览器端可以用 TextDecoder 指定编码
const gbkBytes = new Uint8Array([0xC4, 0xE3, 0xBA, 0xC3]); // "你好" 的 GBK 编码
const text = new TextDecoder("gbk").decode(gbkBytes);
console.log(text); // "你好"
// 然后再用标准方法做 Base64
const base64 = base64EncodeUnicode(text);
避免乱码的实用建议
-
全链路统一 UTF-8:从数据库、后端接口到前端,全部使用 UTF-8 编码,从根源上避免编码不一致。
-
传输时标注编码:如果必须处理多编码数据,在协议或文件头中明确标注字符集(如
Content-Type: text/plain; charset=utf-8)。 -
不要混用编码函数:
btoa/atob只处理 Latin1,处理中文务必走 UTF-8 字节转换。 -
本地处理更安全:Base64 编解码虽然不涉及加密,但如果内容包含敏感信息(如用户数据、内部接口参数),建议使用本地工具处理,避免数据经过第三方服务器。
如果你只是需要快速编解码一段中文文本,可以直接使用我们的 Base64 编解码工具,所有计算都在浏览器本地完成,数据不会上传到任何服务器,粘贴即转换,支持 UTF-8 中文,不会出现乱码问题。