Base64 编码详解:何时以及如何使用
· 12分钟阅读
目录
什么是 Base64 编码?
Base64 是一种二进制到文本的编码方案,它将二进制数据转换为 ASCII 字符串。它仅使用 64 个可打印字符来表示二进制数据:A-Z、a-z、0-9、+ 和 /,并使用 = 进行填充。
这种编码使得通过纯文本通道(如电子邮件、JSON API、XML 文档和 HTML)安全传输二进制数据成为可能。如果没有 Base64,二进制数据可能会被期望文本的系统损坏或误解。
"Base64" 这个名称来源于编码中使用的 64 字符字母表。与加密或哈希不同,Base64 不是一种安全措施——它只是一种将二进制数据表示为文本的方式。任何人都可以在没有任何密钥或密码的情况下将 Base64 解码回其原始形式。
Base64 在现代 Web 开发中无处不在:
- HTML 和 CSS 中用于嵌入图像的数据 URI
- 使用 MIME 编码的电子邮件附件
- 用于身份验证的 JWT(JSON Web Tokens)
- API 身份验证标头(基本认证)
- 在 JSON 或 XML 中存储二进制数据
- 在 Web 表单中编码文件上传
- 证书和密钥文件(PEM 格式)
了解何时以及如何使用 Base64 是任何从事 Web API、数据传输或文件处理工作的开发人员的基本技能。
Base64 的底层工作原理
编码过程将每 3 个字节(24 位)的输入转换为 4 个 Base64 字符(每个 6 位)。这是定义 Base64 的核心数学关系。
以下是一个具体示例的分步过程:
# 步骤 1: 取 3 字节输入
输入文本: "Hi!"
二进制: 01001000 01101001 00100001
# 步骤 2: 分割为 6 位组
010010 000110 100100 100001
# 步骤 3: 将每个 6 位组映射到 Base64 字母表
010010 → S (18)
000110 → G (6)
100100 → k (36)
100001 → h (33)
# 结果: "Hi!" → "SGkh"
Base64 字母表将每个 6 位值(0-63)映射到特定字符:
| 值范围 | 字符 | 描述 |
|---|---|---|
| 0-25 | A-Z | 大写字母 |
| 26-51 | a-z | 小写字母 |
| 52-61 | 0-9 | 数字 |
| 62 | + | 加号 |
| 63 | / | 正斜杠 |
| - | = | 填充字符 |
理解填充
当输入长度不能被 3 整除时,Base64 会添加 = 字符进行填充,使输出长度能被 4 整除。这确保了正确的解码。
# 填充示例
"A" → "QQ==" (1 字节 → 2 字符 + ==)
"AB" → "QUI=" (2 字节 → 3 字符 + =)
"ABC" → "QUJD" (3 字节 → 4 字符,无填充)
填充告诉解码器原始输入中有多少字节。没有它,解码器将不知道最后一组应该期望 1、2 还是 3 个字节。
专业提示: Base64 总是将数据大小增加约 33%。每 3 个字节变成 4 个字符,因此 300KB 的图像在 Base64 编码后会变成大约 400KB。
为什么每个字符 6 位?
选择每个字符 6 位是经过深思熟虑的。使用 6 位,您可以表示 2^6 = 64 个不同的值,这完美映射到 64 字符字母表。
这也意味着 3 字节(24 位)可以均匀地分成四个 6 位块,创建了一个高效的编码方案,开销最小。
实践中的编码和解码
大多数编程语言都提供用于 Base64 编码和解码的内置函数或标准库。以下是流行语言的实际示例:
JavaScript / Node.js
// 浏览器(现代)
const text = "Hello, World!";
const encoded = btoa(text);
console.log(encoded); // SGVsbG8sIFdvcmxkIQ==
const decoded = atob(encoded);
console.log(decoded); // Hello, World!
// Node.js
const buffer = Buffer.from("Hello, World!");
const encoded = buffer.toString('base64');
console.log(encoded); // SGVsbG8sIFdvcmxkIQ==
const decoded = Buffer.from(encoded, 'base64').toString();
console.log(decoded); // Hello, World!
Python
import base64
# 编码
text = "Hello, World!"
encoded = base64.b64encode(text.encode())
print(encoded) # b'SGVsbG8sIFdvcmxkIQ=='
# 解码
decoded = base64.b64decode(encoded).decode()
print(decoded) # Hello, World!
Java
import java.util.Base64;
// 编码
String text = "Hello, World!";
String encoded = Base64.getEncoder().encodeToString(text.getBytes());
System.out.println(encoded); // SGVsbG8sIFdvcmxkIQ==
// 解码
byte[] decoded = Base64.getDecoder().decode(encoded);
System.out.println(new String(decoded)); // Hello, World!
命令行
# 编码
echo -n "Hello, World!" | base64
# SGVsbG8sIFdvcmxkIQ==
# 解码
echo "SGVsbG8sIFdvcmxkIQ==" | base64 -d
# Hello, World!
# 编码文件
base64 image.png > image.txt
# 解码文件
base64 -d image.txt > image.png
对于快速编码和解码任务,您可以直接在浏览器中使用我们的 Base64 编码器 工具,无需编写任何代码。
快速提示: 编码二进制文件时,始终使用原始字节,而不是文本表示。先将二进制转换为文本会损坏数据。
Base64 的常见用例
Base64 编码解决了数据传输和存储中的特定问题。以下是您会遇到或需要使用 Base64 的最常见场景:
1. 嵌入图像的数据 URI
数据 URI 允许您直接在 HTML 或 CSS 中嵌入图像,而无需单独的 HTTP 请求。这对于小图标、徽标或内联 SVG 特别有用。
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..." alt="Red dot" />
/* CSS */
.icon {
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0i...);
}
好处包括减少 HTTP 请求和保证可用性(没有损坏的图像链接)。但是,这会增加 HTML/CSS 文件大小,并阻止浏览器单独缓存图像。
2. 电子邮件附件(MIME)
电子邮件协议是为 7 位 ASCII 文本设计的。Base64 编码允许通过电子邮件系统安全传输 PDF、图像和文档等二进制附件。
MIME(多用途互联网邮件扩展)标准使用 Base64 对电子邮件中的附件进行编码。这由电子邮件客户端和服务器自动处理。
3. API 身份验证
HTTP 基本身份验证在 Authorization 标头中将凭据编码为 Base64:
// 用户名: user, 密码: pass
// 组合: user:pass
// Base64: dXNlcjpwYXNz
Authorization: Basic dXNlcjpwYXNz
请记住:Base64 不是加密。通过 HTTP 的基本认证是不安全的。传输凭据时始终使用 HTTPS。
4. JSON Web Tokens (JWT)
JWT 对标头和有效负载部分使用 Base64URL 编码(一种 URL 安全变体):
// JWT 结构: header.payload.signature
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
每个部分都是 Base64URL 编码的 JSON,使 JWT 可读并可在 URL 和 HTTP 标头中传输。
5. 在数据库中存储二进制数据
某些数据库或数据格式(如 JSON 或 XML)不能很好地处理二进制数据。Base64 编码允许您将二进制数据存储为文本:
{
"user_id": 12345,
"profile_image": "iVBORw0KGgoAAAANSUhEUgAAAAUA...",
"document": "JVBERi0xLjQKJeLjz9MKMSAwIG9iago8..."
}
这在需要包含二进制数据的 NoSQL 数据库、配置文件和 API 响应中很常见。
6. 证书和密钥文件
PEM(隐私增强邮件)格式使用 Base64 对证书、私钥和公钥进行编码:
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAKL0UG+mRKmzMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
...
-----END CERTIFICATE-----
这使得证书和密钥可以在不同系统之间移植,并且易于复制粘贴。
API 和 Web 开发中的 Base64
Web API 经常出于各种目的使用 Base64。理解这些模式可以帮助您有效地使用第三方 API 并设计自己的 API。
文件上传 API
许多 API 接受 JSON 有效负载中的 Base64 编码字符串形式的文件上传:
POST /api/upload
Content-Type: application/json
{
"filename": "document.pdf",
"content": "JVBERi0xLjQKJeLjz9MK...",
"encoding": "base64"
}
这种方法适用于小文件,但对于大型上传来说效率低下。对于超过 1-2MB 的文件,请考虑使用 multipart/form-data 或直接二进制上传。
Webhook 有效负载
Webhook 通常包含 Base64 编码的数据,以确保二进制内容在 JSON 序列化中保持完整:
{
"event": "document.signed",
"document_id": "doc_123",
"signed_pdf": "JVBERi0xLjQKJeLjz9MK...",
"timestamp": "2026-03-31T10:30:00Z"
}
GraphQL 和 Base64
GraphQL 通常使用 Base64 进行基于游标的分页和编码复杂 ID:
query {
users(first: 10, after: "Y3Vyc29yOjEwMA==") {
edges {
cursor
node {
id # 通常是 Base64 编码的: "VXNlcjoxMjM="
name
}
}
}
}
Relay 规范推广了这种模式,使用 Base64 对游标位置和全局 ID 进行编码。
专业提示: 设计 API 时,记录您使用的是标准 Base64 还是 Base64URL 编码。这种差异对 URL 参数很重要,可能会导致细微的错误。
浏览器 API 和 Base64
现代浏览器 API 经常使用 Base64:
// Canvas 转 Base64
const canvas = document.getElementById('myCanvas');
const dataURL = canvas.toDataURL('image/png');
// data:image/png;base64,iVBORw0KGgo...
// 文件转 Base64
const file = document.getElementById('fileInput').files[0];
const reader = new FileReader();
reader.onload = (e) => {
const base64 = e.target.result.split(',')[1];
console.log(base64);
};
reader.readAsDataURL(file);
// 使用 Base64 认证的 Fetch
fetch('/api/data', {
headers: {
'Authorization': 'Basic ' + btoa('username:password')
}
});
性能考虑和开销
Base64 编码带来的权衡会影响性能、带宽和存储。理解这些有助于您就何时使用它做出明智的决定。
大小开销
Base64 将数据大小增加约 33%。以下是计算方法:
- 3 字节输入 → 4 字节输出
- 开销:(4 - 3) / 3 = 33.33%
- 300KB 图像编码后变成约 400KB
- 1MB 文件编码后变成约 1.33MB
| 原始大小 | Base64 大小 | 开销 |
|---|---|---|
| 10 KB | 13.3 KB | +3.3 KB |
| 100 KB | 133 KB | +33 KB |
| 1 MB | 1.33 MB | +333 KB |
| 10 MB | 13.3 MB | +3.3 MB |
| 100 MB | 133 MB | +33 MB |
这种开销对于带宽受限的环境、移动应用程序和大文件传输很重要。
处理速度
编码和解码 Base64 需要 CPU 周期。对于少量数据,这可以忽略不计。对于大文件或高吞吐量系统,它可能成为瓶颈。
现代实现经过高度优化,但如果性能至关重要,您仍应进行基准测试:
// JavaScript 基准测试示例
const data = new Uint8Array(1024 * 1024); // 1MB
crypto.getRandomValues(data);
console.time('encode');
const encoded = btoa(String.fromCharCode(...data));
console.timeEnd('encode');
console.time('decode');
const decoded = atob(encoded);
console.timeEnd('decode');
内存使用
Base64 操作通常需要同时在内存中保存原始数据和编码数据。对于大文件,这可能会使编码/解码期间的内存需求翻倍。
对于大文件,请考虑流式方法:
// Node.js 流式示例
const fs = require('fs');
const { Transform } = require('stream');
fs.createReadStream('large-file.bin')
.pipe(new Transform({
transform(chunk, encoding, callback) {
callback(null, chunk.toString('base64'));
}
}))
.pipe(fs.createWriteStream('large-file.b64'));