btoa 无法直接编码中文或 emoji,因其仅支持 latin-1 字符(0–255),而 unicode 字符需先用 textencoder 转为 utf-8 字节数组,再映射为 latin-1 字符串后调用 btoa。

直接用 btoa 编码中文或 emoji 等非 ASCII 字符会报 InvalidCharacterError,因为这两个函数只接受 Latin-1(即字节值 0–255)范围内的字符串。关键不是“能不能用”,而是“怎么把 Unicode 字符安全转成它能吃的格式”。
为什么 btoa 会拒绝中文?
btoa 名义上叫 “binary to ASCII”,实际处理的是“每个字符对应一个字节”的字符串——也就是把字符串当 Latin-1 编码的字节数组 来读。而中文在 UTF-8 中通常占 3 字节(如“你”是 0xE4 0xBD 0xA0),直接传给 btoa 就等于让它把三个 Unicode 码点(比如 228、189、160)强行当三个 Latin-1 字符去编码,超出 0–255 范围就崩溃。
推荐方案:TextEncoder + btoa(现代标准做法)
这是目前最可靠、语义最清晰的方式,适用于所有现代浏览器(Chrome 63+、Firefox 60+、Safari 15.4+):
- 编码:先用
TextEncoder把字符串转成 UTF-8 字节数组(Uint8Array),再逐字节映射为 Latin-1 字符串,最后btoa - 解码:用
atob得到 Latin-1 字符串 → 转成字节数组 → 用TextDecoder按 UTF-8 解释
封装示例:
function utf8_to_b64(str) {
const bytes = new TextEncoder().encode(str);
let binStr = '';
for (let i = 0; i binStr += String.fromCharCode(bytes[i]);
}
return btoa(binStr);
}
function b64_to_utf8(b64) {
const binStr = atob(b64);
const bytes = new Uint8Array(binStr.length);
for (let i = 0; i bytes[i] = binStr.charCodeAt(i);
}
return new TextDecoder().decode(bytes);
}
用法:utf8_to_b64("你好世界") → "5L2g5aW95LiA55u4",再 b64_to_utf8(...) 完美还原。
兼容旧环境的备选:encodeURIComponent + btoa
如果需支持 Safari 14 或更早版本,可用这个“退而求其次但足够稳”的方案:
- 编码:
btoa(encodeURIComponent(str)) - 解码:
decodeURIComponent(atob(b64))
原理是把中文转成 %E4%BD%A0 这类纯 ASCII 的百分号编码,btoa 就能处理。缺点是结果更长(比如“你”从 3 字节变 9 字符),且需确保前后严格配对,不能漏掉任何一层。
常见踩坑提醒
- 别对 Base64 字符串本身再套一层
btoa—— 接口返回的data:image/png;base64,...要先用.split(",")[1]截掉前缀再atob - Basic Auth 场景下,只要 username/password 是 ASCII(如
admin:pass123),可直接btoa("admin:pass123");含中文或冒号就该换 bearer token -
atob前务必校验字符串是否符合 Base64 格式(长度是 4 的倍数、仅含 A–Z a–z 0–9 + / =),否则必抛错










