一、Base64 是什么
先看一段看起来像乱码的字符串:
SGVsbG8gV29ybGQ=
把它丢进任何 Base64 解码器里,会得到一句话:
Hello World
这就是 Base64——一种把任意二进制数据用 64 个可打印字符表示的编码方式。它的本质是"换一种写法",而不是加密。
那为什么要"换一种写法"?因为早期的很多系统(比如邮件、HTTP、HTML)只能安全地传输 ASCII 字符。如果你直接把一张图片(二进制)塞进邮件里,中间经过的路由器、网关很可能把一些控制字符吃掉,导致接收方拿到的就是损坏的数据。
Base64 的解决办法很简单:把二进制重新编码成纯文本,这样无论经过哪里都不会出问题。
二、为什么叫"Base64"
名字里的 "64",指的是它用了 64 个字符作为"基本字母表":
| 编号 | 字符范围 | 实际字符 |
| 0-25 | 大写字母 | A B C ... Z |
| 26-51 | 小写字母 | a b c ... z |
| 52-61 | 数字 | 0 1 2 ... 9 |
| 62 | 特殊字符 | + |
| 63 | 特殊字符 | / |
| 填充 | 占位符 | =(末尾补齐用) |
为什么是 64 而不是 32 或者 128?因为 64 = 2^6,每 6 位二进制正好对应一个字符。而 6 和 8(一字节 8 位)的最小公倍数是 24,所以每 3 个字节(24 位)可以整齐地拆成 4 个 6 位,对应 4 个 Base64 字符。
三、编码原理:3 字节变 4 字符
这是 Base64 最核心的一步。我们用 "Man" 这三个字符来举例。
第一步,把每个字符转成 ASCII 码:
| 字符 | ASCII(十进制) | 二进制 |
| M | 77 | 01001101 |
| a | 97 | 01100001 |
| n | 110 | 01101110 |
第二步,把这 3 个字节拼成 24 位:
01001101 01100001 01101110
第三步,每 6 位切一刀,切成 4 组:
010011 010110 000101 101110
第四步,每组转成十进制,再查 Base64 字母表:
| 二进制 | 十进制 | 对应字符 |
| 010011 | 19 | T |
| 010110 | 22 | W |
| 000101 | 5 | F |
| 101110 | 46 | u |
所以 "Man" 的 Base64 编码就是 TWFu。
如果原始字节数不是 3 的倍数,最后会补 0 凑齐 6 位,并在结果末尾用 = 补齐到 4 的倍数长度。这就是为什么你经常看到 Base64 字符串末尾有 1 个或 2 个 =。
四、URL 安全的 Base64
标准 Base64 里有两个字符在 URL 里会出问题:
+在 URL 里会被解析成空格/会和路径分隔符冲突
所以有了"URL Safe Base64",把这两个字符换掉:
| 标准字符 | URL 安全字符 |
+ | - |
/ | _ |
末尾的 = 通常也会去掉。JWT(JSON Web Token)里用的就是 URL Safe Base64,因为 token 要放在 URL 或 HTTP 头里传输。
五、Base64 的常见使用场景
| 场景 | 说明 |
| 邮件附件 | MIME 协议规定附件必须用 Base64 编码后嵌入邮件正文 |
| 图片内嵌到 HTML | 把小图标编码成 data:image/png;base64,xxxx,省一次 HTTP 请求 |
| Data URL | CSS 里直接写 background: url(data:image/svg;base64,...) |
| JWT | Header 和 Payload 都用 Base64URL 编码 |
| HTTP Basic Auth | 用户名密码用 Authorization: Basic 发送 |
| 数据库存储二进制 | 某些 NoSQL 数据库只支持字符串,二进制要先 Base64 编码 |
| 配置文件 | 把证书、密钥等二进制内容塞进 JSON/YAML 配置里 |
举个例子,下面是一张 1x1 透明 PNG 内嵌到 HTML 的写法:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVR4nGNgYAAAAAMAASsJTYQAAAAASUVORK5CYII=" />
这样图片就不需要额外的网络请求,直接跟着 HTML 一起下载了。
六、Base64 不是加密
这是一个非常常见的误解。Base64 编码后的字符串看起来像加密,但实际上:
- 算法完全公开,任何人都能解码
- 没有密钥,谁拿到都能还原
- 可逆,编码和解码是严格对应的
它的设计目标只是"让二进制能安全通过文本通道传输",根本不是为了保密。如果你把密码用 Base64 编码后存到数据库里,等同于明文存储——任何人执行一句 echo xxx | base64 -d 就能看到原文。
下面的代码可以让你直观感受到这一点:
import base64
# 编码
encoded = base64.b64encode(b"password123")
print(encoded) # b'cGFzc3dvcmQxMjM='
# 解码
decoded = base64.b64decode(encoded)
print(decoded) # b'password123'
整个过程中没有任何密钥参与,"解密"也就是一行代码的事。
七、Base64 的代价
Base64 不是免费的午餐,它有一个明显的代价:体积会变大 1/3 左右。
因为每 3 个字节会被编码成 4 个字符,所以一段 300KB 的二进制数据,Base64 编码后会变成大约 400KB。在带宽敏感的场景(比如大量图片内嵌到 HTML)需要权衡:
- 小图标(几 KB 以内):内嵌成 Base64 可以减少 HTTP 请求数,整体收益是正的
- 大图片(几十 KB 以上):Base64 后体积膨胀明显,不如直接用图片 URL
八、实践推荐
日常开发中遇到 Base64 的场景非常多——解码接口返回的图片、查看 JWT 内容、把小图标内嵌到 CSS 里等等。手写脚本太麻烦,直接用在线工具最快:
- Base64 编码/解码工具:https://52tool.net/tools/code/base64
支持文本和文件互转、URL Safe 模式切换、实时预览解码结果,调试接口和配置文件时非常方便。