什么是 Base64?深入理解 Base64 编码原理与应用
什么是 Base64?
简单说,Base64 就是一种把二进制数据转换成纯文本字符串的编码方式。
你可能会问:为什么要费这个劲把二进制转成文本?直接传二进制不行吗?
不行。原因很实际——很多传输协议和存储系统在设计之初只考虑了文本。比如说,SMTP 邮件协议最早只能传 ASCII 字符,HTTP 报头的某些字段也只接受可打印字符。你往里头塞二进制数据,轻则乱码,重则协议报错。
Base64 就像是给二进制数据套了一件”文本外衣”,让它能安全地穿过这些只认文本的通道。
说实话,我第一次看到 Base64 的输出结果时,觉得这东西跟乱码没什么两样——一堆大小写字母、数字加上 + 和 /,完全看不出任何规律。但后来理解了它的编码逻辑之后,才发现这玩意儿设计得相当精妙。
Base64 的工作原理
原理不复杂。Base64 的核心思路是:把每 3 个字节(24 位)的数据重新分组为 4 个 6 位的单元,每个单元映射到一个可打印字符上。
我们一步步来看这个过程。
第一步:准备数据
假设我们要编码”Man”这个单词。在计算机里,这三个字母对应的字节是:
| 字符 | ASCII 十进制 | 十六进制 |
|---|---|---|
| M | 77 | 0x4D |
| a | 97 | 0x61 |
| n | 110 | 0x6E |
第二步:拼成 24 位
把这三个字节的二进制串在一起:
M -> 0 1 0 0 1 1 0 1
a -> 0 1 1 0 0 0 0 1
n -> 0 1 1 0 1 1 1 0
连起来就是:01001101 01100001 01101110
第三步:切成 6 位一组
把这段 24 位的比特串按 6 位一切:
010011 010110 000101 101110
第四步:映射到 Base64 字符表
6 位能表示的范围是 0 到 63,正好 64 个值,对应 Base64 的 64 个字符:
数值 0-25 → A-Z
数值 26-51 → a-z
数值 52-61 → 0-9
数值 62 → +
数值 63 → /
上一步的四个值分别是:
010011 = 19 → T
010110 = 22 → W
000101 = 5 → F
101110 = 46 → u
所以,“Man” 的 Base64 编码结果是 TWFu。
等号是怎么来的?
三个字节正好能分成 4 个 6 位单元,但如果数据长度不是 3 的倍数呢?比如只有一个字节”a”:
a -> 01100001 -> 切了加补零 -> 011000 010000 -> Y Q
但只有 1 个字节成了 2 个编码字符,少了 2 个,所以补两个 =:
a -> "YQ=="
两个字节的话补一个 =。这个等号就是 Base64 的填充符——它告诉解码器”这里没有实际数据,别解析”。
核心特性
Base64 有几个值得记住的特点:
| 特性 | 说明 |
|---|---|
| 体积膨胀 | 编码后比原文大约 33%(3 字节变 4 字节) |
| 字符集 | A-Z、a-z、0-9、+、/、= |
| 可逆性 | 完全可逆,编码和解码是一一对应的 |
| 安全性 | 不是加密!不提供任何保密性,只是编码转换 |
| URL-safe 变体 | 把 +/ 换成 -_,去掉填充 =,适用于 URL 参数 |
体积膨胀这件事,我之前在做一个图片上传接口时踩过坑——前端用 FileReader 读出来的 Base64 字符串比我预期的大了不少,导致 POST 请求超时。后来一算才发现,一张 2MB 的图片转 Base64 后差不多变成 2.7MB,再加上 JSON 序列化开销,实际传输量接近 3MB。后来改成了先用 FormData 上传原文件,后端再处理,性能立刻好了很多。
实际应用场景
1. 邮件附件(MIME)
这是 Base64 的”娘家”——MIME(Multipurpose Internet Mail Extensions)标准规定,邮件正文和附件的二进制数据用 Base64 编码传输。因为它只用了可打印的 ASCII 字符,不会破坏邮件的文本协议。
2. Data URI
前端开发中,你可能经常见到这种写法:
<img src="data:image/png;base64,iVBORw0KGgo...">
把小图标、logo 直接内嵌到 HTML 或 CSS 里,省掉一个 HTTP 请求。不过要我说,这招只适合小文件——超过 5KB 的图片就别这么干了,Base64 体积膨胀加上浏览器缓存颗粒度的问题,反而得不偿失。
3. JWT Token
JSON Web Token 的三段式结构中,Header 和 Payload 都是 Base64Url 编码的:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U
三段分别是 Base64(Header) + Base64(Payload) + Signature。注意这里用的是 URL-safe 版本,把 + 换成 -,/ 换成 _,并且去掉了 = 填充符。为什么?因为 JWT 通常在 HTTP 请求头、URL 参数里传递,这些位置对 + 和 / 字符比较敏感。
4. 浏览器内置函数
浏览器提供了两个原生的 Base64 API:
// 编码
const encoded = btoa('Hello, World!'); // "SGVsbG8sIFdvcmxkIQ=="
// 解码
const decoded = atob(encoded); // "Hello, World!"
但要注意,btoa() 只支持 ASCII 字符。你要编码中文的话,必须先手动转成字节数组:
function utf8ToBase64(str) {
const bytes = new TextEncoder().encode(str);
const binStr = String.fromCharCode(...bytes);
return btoa(binStr);
}
这是不止一个人踩过的坑——直接 btoa("你好") 会报 InvalidCharacterError。
5. API 响应中的二进制字段
有些 REST API 在 JSON 响应里嵌入图片、音频片段等二进制数据时,会选择 Base64 编码。比如,某些 OCR 接口返回的识别结果会附上一个裁剪后的 Base64 图片。好处是简单——一个 JSON 对象全搞定,不需要多部分响应或额外的文件下载。不过缺点也很明显:刚才说的体积膨胀和编解码开销。
常见误区与陷阱
误区一:Base64 是加密
这是我见过最多的误解。说实话,很多初级开发者一看到不可读的字符串就觉得是加密了。Base64 完全不提供安全性——任何人都能解码。它的目的不是把数据藏起来,而是让二进制数据能通过文本通道。
我之前见过一个项目,用 Base64 编码来”保护” API 认证信息——把用户名密码 Base64 编码后放在 HTTP Header 里当令牌用。这跟明文存储几乎没区别,最多就算个”障眼法”。
误区二:Base64 能压缩数据
恰恰相反。Base64 编码后数据是变大的,不是变小。规律的比例是 4:3——每 3 个字节变成 4 个字节,增加约 33%。如果数据长度不是 3 的倍数还要加填充符,膨胀率更高。
误区三:所有 Base64 都是一样的
严格来说不是。标准的 Base64 用 +/ 做最后两个字符,但还有其他变体:
- Base64URL:
+/换-_,去掉= - Base64 (MIME):每行 76 个字符,以
\r\n换行 - Radix-64:OpenPGP 使用的变体,带 CRC24 校验和
用的时候要搞清楚对方期望的是哪个版本。我做 JWT 解析时就被这个坑过——后端用的是标准 Base64,前端库用的是 Base64URL,解出来的 Header 直接乱码,排查了半天才发现是变体不匹配。
Base64 vs 替代方案
| Base64 | Hex (Base16) | Base32 | |
|---|---|---|---|
| 字符集大小 | 64 个 | 16 个 | 32 个 |
| 膨胀率 | ~33% | 100% | ~60% |
| 大小写敏感 | 是(标准) | 否 | 否 |
| 易读性 | 中 | 高 | 较高 |
| 主要用途 | MIME、JWT、Data URI | 哈希表示、调试 | OTP 密钥、人类友好场景 |
Hex 的膨胀率是 100%——每个字节变成两个十六进制字符,空间效率明显不如 Base64。但 Hex 的好处是:它完全没有大小写歧义,适合做哈希值的显示。你去看 Git 的 commit hash 或者 MD5 的输出,都是 Hex 格式。
Base32 在大小写上也不敏感(通常用大写),所以更适合需要口头传达或手动输入的场合,比如两步验证密钥。像 GitHub 的 recovery code 就是 Base32 编码。
常见问题
Q: 为什么是 64 个字符?不是 63 也不是 65?
因为 2^6 = 64。把二进制数据按 6 位分组,刚好需要 64 个字符来一一对应。如果用 5 位就是 32 个字符(Base32),但膨胀率会更高。
Q: Base64 编码后一定比原文长吗?
是的,一定。最理想的情况(原文长度是 3 的倍数)膨胀约 33%。不是什么意外——3 字节变 4 个字符,4/3 ≈ 1.33,就是 33% 的增量。
Q: 浏览器里的 btoa 和 atob 命名是什么意思?
btoa = “binary to ASCII”,atob = “ASCII to binary”。说来好笑,我第一次用的时候死活记不住哪个是编码哪个是解码,后来把它理解为 b-to-a(B 到 A)和 a-to-b(A 到 B)就行了。
Q: Base64 编码的 PDF 或图片可以直接用吗?
在 HTML 的 <img> 标签或 <iframe> 里可以(配合 Data URI),但不能直接把 Base64 字符串当成文件打开。你需要先解码成二进制,再保存为对应格式的文件。
Q: 有没有比 Base64 更高效的选择?
如果不需要纯 ASCII 传输,直接传二进制永远是最优的,体积零膨胀。如果一定需要文本编码但可以接受更大的字符集,Base85(Ascii85)大约只膨胀 25%。不过 Base85 的实现比 Base64 复杂不少,不如 Base64 普及。