Base64 在 API 开发中无处不在——HTTP Basic Auth 用它编码凭证,富文本编辑器用它传图片,文件上传接口也有 Base64 模式。但 Base64 上传和 multipart 上传哪个更好?Basic Auth 的 Base64 安全吗?本文把 API 场景中的 Base64 用法讲透。
HTTP Basic Auth 中的 Base64
HTTP Basic Authentication 用 Base64 编码 用户名:密码 字符串,放在 Authorization 请求头中。
GET /api/profile HTTP/1.1
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
Base64 编码部分解码后是 username:password:
// 编码
const credentials = btoa('admin:mySecret123')
// YWRtaW46bXlTZWNyZXQxMjM=
fetch('/api/profile', {
headers: {
'Authorization': `Basic ${credentials}`
}
})
// 解码
atob('YWRtaW46bXlTZWNyZXQxMjM=')
// admin:mySecret123
安全提醒:Base64 编码不是加密!任何人拦截到这个请求头,1 秒内就能解码出明文密码。Basic Auth 必须配合 HTTPS 使用,否则凭证会泄露。
用 curl 测试 Basic Auth 接口:
# 用 -u 参数,curl 自动做 Base64 编码
curl -u admin:mySecret123 https://api.example.com/profile
# 等价于手动编码
curl -H "Authorization: Basic $(echo -n 'admin:mySecret123' | base64)" https://api.example.com/profile
Base64 文件上传 vs multipart 上传
文件上传有两种方案:Base64 字符串和 multipart/form-data。
方案一:Base64 字符串上传
// 前端:把图片转为 Base64 字符串
const file = document.querySelector('input[type=file]').files[0]
const reader = new FileReader()
reader.onload = () => {
const base64 = reader.result.split(',')[1]
fetch('/api/upload', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
filename: file.name,
data: base64 // Base64 字符串
})
})
}
reader.readAsDataURL(file)
# 后端:Python Flask 接收 Base64 文件
import base64
from flask import Flask, request
@app.route('/api/upload', methods=['POST'])
def upload():
data = request.json
file_data = base64.b64decode(data['data'])
with open(f"uploads/{data['filename']}", 'wb') as f:
f.write(file_data)
return {'status': 'ok'}
方案二:multipart/form-data 上传
// 前端:FormData 直接传二进制
const file = document.querySelector('input[type=file]').files[0]
const formData = new FormData()
formData.append('file', file)
fetch('/api/upload', {
method: 'POST',
body: formData // 不需要设置 Content-Type,浏览器自动设置
})
# 后端:Python Flask 接收 multipart 文件
@app.route('/api/upload', methods=['POST'])
def upload():
file = request.files['file']
file.save(f"uploads/{file.filename}")
return {'status': 'ok'}
两种方案对比
| 体积 | 原始 + 33% | 原始大小 |
| 编码开销 | 前端编码 + 后端解码 | 无额外开销 |
| 适用场景 | JSON API、小程序 | 表单、传统 Web |
| 浏览器支持 | 所有环境 | 所有环境 |
| 多文件 | 需要多次编码 | 一次 FormData 携带多文件 |
| 断点续传 | 容易(字符串分段) | 需要额外方案 |
结论:大多数场景用 multipart/form-data 更好——体积更小、无需编解码开销。Base64 上传适合小程序(不支持 FormData)、JSON API 需要把文件嵌入 JSON、或者需要把图片数据嵌入富文本的场景。
富文本编辑器中的图片传输
富文本编辑器(如 TinyMCE、Quill)插入图片时,通常把图片转为 Base64 嵌入 HTML:
// Quill 编辑器:插入 Base64 图片
const quill = new Quill('#editor', {
modules: { toolbar: true }
})
// 监听图片上传
document.getElementById('image-input').addEventListener('change', (e) => {
const file = e.target.files[0]
const reader = new FileReader()
reader.onload = () => {
const dataUri = reader.result // data:image/png;base64,…
// 插入编辑器
const range = quill.getSelection()
quill.insertEmbed(range.index, 'image', dataUri)
}
reader.readAsDataURL(file)
})
编辑器输出的 HTML 包含 Base64 内联图片:
<p>文章正文 <img src="data:image/png;base64,iVBORw0KGgo…"></p>
问题:大图片的 Base64 字符串很长,存入数据库会增大存储压力,且前端渲染时阻塞页面。
解决方案:编辑器中先用 Base64 预览,保存时上传到服务器替换为 URL:
async function saveArticle() {
const html = quill.root.innerHTML
// 找到所有 Base64 图片,上传到服务器替换为 URL
const imgRegex = /src="(data:image\\/[^;]+;base64,[^"]+)"/g
const matches = […html.matchAll(imgRegex)]
for (const match of matches) {
const dataUri = match[1]
const base64 = dataUri.split(',')[1]
const mime = dataUri.match(/data:(image\\/[^;]+);/)[1]
// 上传到服务器
const formData = new FormData()
const blob = await (await fetch(dataUri)).blob()
formData.append('file', blob, `image.${mime.split('/')[1]}`)
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
})
const { url } = await response.json()
// 替换 HTML 中的 Base64 为服务器 URL
html = html.replace(dataUri, url)
}
// 保存到数据库的 HTML 中不再有 Base64
await fetch('/api/articles', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: html })
})
}
接口签名中的 Base64
API 签名验证中常用 HMAC + Base64 的组合:
const crypto = require('crypto')
// 生成签名
function generateSignature(params, secret) {
const sortedParams = Object.keys(params)
.sort()
.map(k => `${k}=${params[k]}`)
.join('&')
return crypto
.createHmac('sha256', secret)
.update(sortedParams)
.digest('base64') // HMAC 结果转 Base64
}
// 请求
const params = { timestamp: '1693123456', nonce: 'abc123' }
const signature = generateSignature(params, 'api_secret')
fetch('/api/data', {
headers: {
'X-Timestamp': params.timestamp,
'X-Nonce': params.nonce,
'X-Signature': signature // Base64 编码的 HMAC 签名
}
})
为什么签名用 Base64? HMAC 计算结果是二进制字节(32 字节),直接放在 HTTP 头中会有非 ASCII 字符问题。Base64 编码后变成安全的 ASCII 字符串,可以放在 Header 中。
各场景方案速查
| 文件上传 | multipart/form-data | 体积小、无编解码开销 |
| JSON API 传文件 | Base64 嵌入 JSON | JSON 不支持二进制 |
| HTTP Basic Auth | Base64 编码凭证 | 规范要求,需配合 HTTPS |
| 接口签名 | HMAC + Base64 | 二进制签名转 ASCII |
| 富文本图片 | 先 Base64 预览 → 保存时上传替换 | 避免存储大 Base64 |
| 小程序文件上传 | Base64 字符串 | 小程序不支持 FormData |
| WebSocket 传二进制 | Base64 或直接 ArrayBuffer | 取决于协议设计 |
在线验证
理解 API 场景中的 Base64 用法,可以用在线工具快速验证。可以在 盘子工具站 Base64 编码工具 上做这种验证——输入 admin:mySecret123,获得 YWRtaW46bXlTZWNyZXQxMjM=,这就是 HTTP Basic Auth 的 Authorization 头内容。
工具底部的"文件转 Base64"功能可以模拟 Base64 文件上传场景——上传一个文件,获得 Base64 字符串,理解 Base64 上传时传输的数据量和原始文件大小的 1.33 倍关系。
如果你用 盘子工具站 JWT 解析工具 解码一个 JWT Token,
Header 中的 alg 字段说明签名算法,而 Signature 段就是 Base64URL 编码的 HMAC 二进制签名——和本文接口签名部分的原理一致。两个工具配合使用,能理解 Base64 在 API 生态中的多重角色。所有编解码在浏览器本地完成,不上传服务器。




