Base64 是什么
Base64 是一种将二进制数据用 64 个可打印 ASCII 字符表示的编码方案。这 64 个字符包括:
A-Z(26 个大写字母)a-z(26 个小写字母)0-9(10 个数字)+和/(2 个特殊符号)
加上填充字符 =,构成了完整的 Base64 字符表。它的核心作用是让二进制数据能安全通过只支持文本的通道传输,例如电子邮件、JSON、URL、HTTP 头部等。
为什么需要 Base64
许多协议和历史系统设计为只处理 ASCII 文本。如果直接传输二进制字节,可能出现:
- 字节流中的
0x00(空字符)被当作字符串结尾截断 - 控制字符(如
0x0A换行、0x0D回车)破坏协议解析 - 非 ASCII 字节在不同编码下被错误解释
Base64 把任意字节流转换为纯 ASCII 可见字符,绕开这些问题。代价是体积膨胀约 33%(每 3 字节编码为 4 字符)。
编码原理
Base64 将输入字节按 每 3 字节(24 位)一组 重新切分为 4 个 6 位块,每个 6 位块映射到字符表中的一个字符:
原始字节: 01000001 01000010 01000011 (A B C, 3 字节)
重新分组: 010000 010100 001001 000011
十进制: 16 20 9 3
字符表: Q U J D
结果: "QUJD"
填充规则:当输入字节数不是 3 的倍数时:
- 剩 1 字节:编码为 2 个字符 +
== - 剩 2 字节:编码为 3 个字符 +
=
例如 "A"(1 字节)编码为 "QQ==","AB"(2 字节)编码为 "QUI="。
JavaScript 中的 Base64 API
基础 API:btoa / atob
浏览器提供 btoa(binary to ASCII)和 atob(ASCII to binary)两个全局函数:
const encoded = btoa('Hello'); // "SGVsbG8="
const decoded = atob('SGVsbG8='); // "Hello"
中文陷阱:btoa 不能直接处理中文
btoa 只接受 Latin1 字符(码点 ≤ 255)。直接编码中文会抛错:
btoa('你好'); // Uncaught DOMException: Failed to execute 'btoa'
原因:JavaScript 字符串使用 UTF-16,中文字符码点超过 255,btoa 无法处理。
正确方案:TextEncoder + Uint8Array
用 TextEncoder 将字符串转为 UTF-8 字节序列,再逐字节转换为 Latin1 字符串后编码:
// 编码:支持中文、Emoji 等任意 Unicode 字符
function encodeBase64(str) {
const bytes = new TextEncoder().encode(str);
// 将 UTF-8 字节流按 Latin1 字符串处理,再交给 btoa
let binary = '';
for (const b of bytes) {
binary += String.fromCharCode(b);
}
return btoa(binary);
}
// 解码:还原 UTF-8 字节流
function decodeBase64(b64) {
const binary = atob(b64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
return new TextDecoder().decode(bytes);
}
encodeBase64('你好 🌍'); // "5L2g5aW9IPCfjoI="
decodeBase64('5L2g5aW9IPCfjoI='); // "你好 🌍"
大文本分块优化
String.fromCharCode.apply 在超长数组上会触发调用栈溢出。生产环境应分块处理:
function encodeBase64Large(str) {
const bytes = new TextEncoder().encode(str);
const CHUNK = 0x8000; // 32KB 一块
let binary = '';
for (let i = 0; i < bytes.length; i += CHUNK) {
binary += String.fromCharCode.apply(null, bytes.subarray(i, i + CHUNK));
}
return btoa(binary);
}
URL 安全变体
标准 Base64 中的 + 和 / 在 URL 中有特殊含义(+ 表示空格,/ 是路径分隔符),会导致解析错误。URL 安全 Base64 做两个替换:
+→-/→_- 通常去掉末尾的
=
function toUrlSafe(b64) {
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
function fromUrlSafe(b64) {
// 补齐填充
let str = b64.replace(/-/g, '+').replace(/_/g, '/');
while (str.length % 4) str += '=';
return str;
}
URL 安全变体常见于 JWT(JSON Web Token)、Data URL、文件名生成等场景。
Base64 的常见应用场景
1. 图片内联(Data URL)
小图标可以直接内联到 HTML/CSS 中,减少 HTTP 请求:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg..." alt="图标" />
.icon {
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxu...');
}
注意:仅适合几 KB 的小图。大图用 Base64 反而增大体积(+33%),且无法被浏览器缓存。
2. JWT(JSON Web Token)
JWT 的 Header 和 Payload 使用 URL 安全 Base64 编码:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.signature
3. 邮件附件(MIME)
SMTP 协议只支持 ASCII,邮件附件通过 Base64 编码后传输。
4. 在 JSON 中嵌入二进制
JSON 不支持原生二进制,文件上传、加密结果等需以 Base64 字符串形式携带。
5. Source Map 与字体文件
Webpack/Vite 的 source map、WOFF 字体嵌入都使用 Base64。
Base64 常见陷阱
| 陷阱 | 说明 | 解决方案 |
|---|---|---|
| 中文报错 | btoa 不支持 UTF-16 字符 | 用 TextEncoder 转 UTF-8 |
| 体积膨胀 | 编码后体积 +33% | 大文件避免使用 |
| 不可加密 | Base64 是编码不是加密 | 敏感数据需额外加密 |
| 填充丢失 | 去 = 后部分库无法解码 | 解码前补齐 = |
| 编码混淆 | 看起来像乱码易误判为加密 | 文档明确标注是 Base64 |
Base64 vs Hex vs Base32
| 编码 | 字符集 | 体积膨胀 | 适用场景 |
|---|---|---|---|
| Base64 | 64 字符 | +33% | 通用,最大密度 |
| Base32 | 32 字符 | +60% | 大小写不敏感场景 |
| Hex | 16 字符 | +100% | 哈希摘要、密钥展示 |
选择依据:密度优先选 Base64,兼容性优先选 Hex。
总结
- Base64 用于让二进制安全通过文本通道,不是加密
- 编码原理:3 字节 → 4 字符,不足补
= - 处理中文必须用
TextEncoder转 UTF-8,再交给btoa - URL 场景使用 URL 安全变体(
-和_) - 大文件慎用,体积膨胀 33% 且无法缓存
想要快速编解码 Base64?试试我们的 Base64 在线工具,支持中文、Emoji、URL 安全变体,所有数据在浏览器本地处理,零上传零追踪。