技术笔记

Base64 为什么是四个字符一组

三个字节是 24 位,刚好拆成四个 6 位索引。这篇把分组、填充和 URL 安全变体讲完,并对照 JWT 与 Data URL 里各自省略了什么。

作者 Brook/更新于 2026-10-09/约 10 分钟

二进制为什么要先变成文本

很多通道只保证能安全运送一部分字符:JSON 字符串、URL、HTTP 头、放在 HTML 属性里的值。原始字节里可以出现 0、换行和各种控制字符,直接放进去会截断字段或被对方当成别的语法。Base64 把任意字节映射到 64 个不会惹事的字符上,代价是体积大约变成原来的 4/3。

它是编码,不是压缩,也不是加密。编码后的文本谁都能解回原来的字节。如果目标是让别人读不懂,Base64 没有提供这件事。如果目标是让体积变小,它还会变大。它只解决「这段字节能不能放进文本通道」。

24 位拆成四个索引

  1. 01字节3 字节 = 24 位不足 3 字节就先记下还差几个。
  2. 02切开4 个 6 位每个 6 位的值落在 0 到 63。
  3. 03查表A–Z a–z 0–9 + /64 个字符各对应一个索引。
  4. 04补齐用 = 填满 4 个字符缺 1 字节补两个 =,缺 2 字节补一个 =。
图 1 一组始终消耗 3 个字节、产出 4 个字符。不够 3 字节时用填充补齐组。

6 位正好能表示 0 到 63,所以字母表长度是 64,不是 256。三个字节对齐之后没有余数。输入长度不是 3 的倍数时,最后一组凑不满:还差一个字节省略两个字符的位置,用 = 占着;还差两个字节则补一个 =。填充让解码器知道最后一组有多少位是真实数据。没有这个约定,末尾的 0 位会被当成真实的空字节。

标准字母表的最后两个字符是 + 和 /。解码时遇到表以外的字符应该失败,而不是跳过。空白是否容忍,各实现不一样。稳妥的做法是先去掉明确的换行和空格,再解码,并且把这个预处理说出来。不要默默丢掉奇怪的字符,那会把损坏的数据解成另一份字节。

URL 安全变体改了两个字符

+ 在查询串里可能被读成空格,/ 在路径里是分隔符。把 Base64 放进 URL 时,URL 安全变体用 - 代替 +,用 _ 代替 /。比特分组完全一样,只是字母表的最后两项换了名字。填充 = 在 URL 里也麻烦,所以有的格式会把末尾的 = 去掉,解码前再按长度补回来:长度除以 4 余 2 补两个,余 3 补一个,余 1 是非法的。

JWT 用的就是这套:Base64URL,并且通常去掉填充。把 JWT 的一段直接丢给只认 +/ 和强制填充的解码器,会失败。先替换字符、再补 =,得到的字节才和标准 Base64 相同。反方向也一样,不能只换算法名不换字母表。

Data URL 和 JWT 在 Base64 外面又包了一层

标准 Base64

字母表含 + /,常带 =。

  • 适合 JSON 字符串
  • 不适合直接放进 URL
  • 解码结果是原始字节

Base64URL

字母表含 - _,常省略 =。

  • JWT 的每一段
  • 要先还原填充
  • 不要把签名段当 JSON

Data URL

data: 类型;base64, 后面才是数据。

  • 逗号之前都是头
  • 只解码逗号后面
  • 类型说明字节该怎么解释
图 2 中间那一段才是 Base64。前后的包装要先剥掉,否则会把包装字符也解码进字节里。

一张粘进网页的小图常常是 data:image/png;base64,......。逗号前面声明了媒体类型和编码方式,逗号后面才是 Base64。如果把整串包括 data: 一起解码,开头会多出一堆头字节,图片文件头对不上。文本类的 Data URL 有时不用 Base64,而用百分号编码,要先看头里有没有 base64 这个标记。

解码失败时按这个顺序查

失败信息通常只说「不是合法的 Base64」。它不会告诉你错在包装、字母表还是填充。按下面的顺序排除,比反复换一个解码器试,更快定位是哪一层包装没剥掉。

  • 是不是还包着 Data URL、引号或换行。先剥到只剩字母表里的字符。
  • 字母表是标准的还是 URL 安全的。+ / 和 - _ 不要同时出现在一份正规数据里。
  • 长度是不是差填充。去掉 = 之后,长度 mod 4 不能是 1。
  • 解出来的是字节。如果期望看到文本,还要用 UTF-8 解释一次,解释失败是编码问题,不是 Base64 问题。

本站的 Base64 工具按字节做标准编码和解码,JWT 工具单独处理 Base64URL 和无填充。两边不要混用同一份粘贴:先看清这串字符是要进 URL、进 JSON,还是要还原成文件字节。

文中对应的工具

继续读