Base64:用 33% 的体积膨胀,买一张在文本世界通行的门票

如果你见过 JWT 里那一长串 eyJhbGciOiJIUzI1NiIs...,或者在 JSON 里塞过图片的字符串,或者在 PEM 证书里复制过 BEGIN PUBLIC KEY 下面那堆乱码------你已经和 Base64 打过无数次交道了。但很多人只是"会用",并不理解它到底在做什么,以及为什么明明会让数据变大,我们却离不开它。

这篇文章把 Base64 的编码原理和工程逻辑彻底讲清楚。


一、Base64 到底在解决什么问题?

计算机世界里有两套"语言体系":

  • 二进制世界 :图片、音频、加密后的密文、protobuf 消息......它们是任意的 0 和 1,可能包含 \0<>&、控制字符等"危险分子"。
  • 文本世界 :JSON、XML、HTML、URL、邮件协议、配置文件......它们只认可打印的 ASCII 字符,看到 \0 就截断,看到 < 以为是标签开头,看到 & 以为是转义符。

Base64 是一座桥:它把二进制数据翻译成文本世界能安全识别的字符,让二进制数据能在邮件里发、在 JSON 里传、在 URL 里带、在配置文件里存。

代价是体积膨胀约 33% (3 字节二进制变成 4 字节 ASCII)。但工程上,兼容性往往比带宽更贵


二、编码原理:6 位一组的"切片游戏"

为什么是 6 位?

ASCII 有 128 个字符,但安全、无歧义、可打印的只有 64 个:

  • 大写 A-Z(26 个)
  • 小写 a-z(26 个)
  • 数字 0-9(10 个)
  • 符号 +/(2 个)

2⁶ = 64,所以 6 位二进制正好能映射到一个安全字符。

核心过程:3 字节 → 4 字符

以经典的 "Man" 为例:

第一步:转成二进制

字符 ASCII 码 二进制
M 77 01001101
a 97 01100001
n 110 01101110

拼接成 24 位:01001101 01100001 01101110

第二步:按 6 位重新切分

复制代码
010011 | 010110 | 000101 | 101110
  19   |   22   |    5   |   46

第三步:查 Base64 索引表

索引 字符
0-25 A-Z
26-51 a-z
52-61 0-9
62 +
63 /
r 复制代码
19 → T,  22 → W,  5 → F,  46 → u

结果"Man""TWFu"


三、补零与补等号:不足 3 字节怎么办?

Base64 的基本单位是 24 位(3 字节 × 8 位 = 4 组 × 6 位)。如果原始数据不是 3 的倍数,就在末尾补 0,凑成 24 位,输出后再用 = 标记补了多少。

原始字节数 补齐后 输出字符数 结尾
3 无需补齐 4
2 补 8 个 0 → 24 位 3 =
1 补 16 个 0 → 24 位 2 ==

示例:"M"(只有 1 字节)

ini 复制代码
M = 01001101
补齐:01001101 00000000 00000000
切分:010011 | 010000 | 000000 | 000000
索引:19     | 16     | 0      | 0
字符:T      | Q      | =      | =

结果:"M""TQ=="

那个 = 不是数据的一部分,只是填充标记,解码时会丢弃。


四、为什么体积大了 33%,我们还离不开它?

Base64 不是"最优解",而是**"工程上最可靠的兼容解"**。以下场景它几乎是标准答案:

1. Data URI:把资源直接嵌进代码

html 复制代码
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...">

一张 1KB 的小图标,Base64 后变成 1.33KB,但省掉了一次 HTTP 请求。对首屏关键资源来说,零延迟比 300 字节体积重要得多。

2. JWT:在 HTTP Header 里传"自包含令牌"

text 复制代码
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.SflKxwRJSMeKKF2QT4fwpMe...

JWT 的 Header 和 Payload 本质是 JSON,但必须放在 HTTP Header 这种纯文本环境 里。Base64URL(把 + 换成 -/ 换成 _)是 JWT 的标准编码方式。

3. 证书与密钥:PEM 格式

text 复制代码
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----

DER 二进制证书被 Base64 包裹后,可以安全地复制粘贴到聊天框、邮件、GitHub Issue、配置文件里,不会因为字符集转换而损坏。

4. JSON 里的二进制字段

前后端交互时,图片、文件、加密数据往往这样传:

json 复制代码
{
    "filename": "avatar.png",
    "content": "iVBORw0KGgoAAAANSUhEUgAAADIA..."
}

虽然直接传二进制更高效,但整个 JSON.stringify + json.Unmarshal 生态都是文本的。Base64 是最省事的兼容方案

5. 邮件附件:MIME 标准

早期邮件网关只支持 7-bit ASCII,MIME 标准强制用 Base64 编码附件。这个历史包袱至今仍在------你发邮件带 PDF,底层还是 Base64。


五、什么时候不该用 Base64?

场景 替代方案 原因
微服务内部 RPC Protobuf / gRPC 二进制直接传,零膨胀,解析快 10 倍
大文件传输(>10MB) 分片二进制流 33% 膨胀是实打实的带宽和存储成本
高频低延迟交易 固定二进制协议 编码解码耗时不可接受
对象存储(S3/OSS) 直接上传二进制 存储按量计费,Base64 不划算

原则:如果通信双方都能处理裸二进制,就不要用 Base64 给自己加戏。


六、Go 实战:标准库零值可用

Go 的 encoding/base64 包设计非常简洁,零值可用

go 复制代码
package main

import (
	"encoding/base64"
	"fmt"
)

func main() {
	data := []byte("Man")

	// 标准编码
	encoded := base64.StdEncoding.EncodeToString(data)
	fmt.Println(encoded) // TWFu

	// 解码
	decoded, _ := base64.StdEncoding.DecodeString(encoded)
	fmt.Println(string(decoded)) // Man

	// URL 安全编码(JWT 专用)
	urlSafe := base64.URLEncoding.EncodeToString([]byte("hello+world/test"))
	fmt.Println(urlSafe) // aGVsbG8rd29ybGQvdGVzdA==
}

两个编码器的区别

标准 URL 安全
+ + -
/ / _
padding = 可选去掉

JWT 用的是 base64.URLEncoding,因为标准 Base64 的 +/ 在 URL 里会被转义成 %2B%2F,体积反而更大。


七、总结

Base64 的本质不是压缩,也不是加密,而是编码------它用一种文本世界能读懂的字母表,复述了一遍二进制数据的内容。

  • 原理 :3 字节拆成 4 个 6 位组,每组查表变成一个 ASCII 字符,不足补 =
  • 代价:体积膨胀 33%。
  • 收益 :二进制数据能在 JSON、URL、邮件、HTML、配置文件里无损、无歧义地通行
  • 原则:对外兼容用 Base64,对内性能用二进制。

你在工作中遇到过哪些 Base64 的诡异 bug?比如忘了用 URL 安全版本导致 JWT 解析失败,或者把 Base64 当加密算法用?欢迎在评论区分享。


相关推荐
得物技术1 小时前
HorizonVault 技术深潜:如何在 HDD 上做出 100GB/s+ 级大吞吐分布式存储|得物技术
大数据·后端·kafka
码不停蹄的玄黓1 小时前
SpringBoot 自动装配原理
java·spring boot·后端
XovH1 小时前
Docker 从 0 到 1 再到 Kubernetes 实战:深入理解 Docker 镜像和分层结构
后端
来自上海的这位朋友1 小时前
用 Three.js 做一个 Web 3D 非对称追猎 Demo:从场景、角色到手感调试
后端·游戏开发·three.js
SimonKing1 小时前
别再把业务逻辑写进回调接口了!支付回调的正确打开方式
java·后端·程序员
来自上海的这位朋友1 小时前
Spring Boot + MySQL 搭一个多人游戏后端:登录、房间、匹配、对局和成长系统
前端·后端·three.js
来自上海的这位朋友1 小时前
浏览器里的实时对局同步:WildHunt 的 WebSocket、输入序号与服务端快照
前端·javascript·后端
chasdream2 小时前
Doris批量导入慢?Spring Boot整合Doris Routine Load是如何提升数据导入性能
后端·数据分析
用户2181697049302 小时前
golang 并发 goroutine sync.Lock atomic WaitGroup 协程通信(共享数据,channqel消息)channel
后端