atob解码Base64得到的是UTF-16字符串而非二进制数据,直接用于Blob会出错;正确做法是用Uint8Array.from(atob(base64), c => c.charCodeAt(0))转为字节数组再构造Blob。

atob 解码后得到的是字符串,不是二进制数据
atob 只能将 Base64 字符串解码为原始字符串(UTF-16 编码的 JavaScript 字符串),而图片等二进制数据在 Base64 编码前是字节流,直接用 atob 解码会导致高位字节被错误解释为 Unicode 码点,产生乱码或数据损坏。例如,一个 Uint8Array 中值为 0xFF 的字节,在 atob 后可能变成 \uFFFD 或截断。
常见错误现象:
- 图片解码后无法用 URL.createObjectURL(new Blob([str])) 正常显示
- 控制台报错 Failed to execute 'createObjectURL' on 'URL': Overload resolution failed
- 解码后的长度与原始字节数不一致(str.length !== originalByteLength)
正确做法是把 atob 的输出转成真正的字节数组:
- 先用
atob得到字符串 - 再遍历每个字符,用
.charCodeAt(0)取出 UTF-16 码点(注意:此时每个字符对应 1 个原始字节,前提是原始 Base64 确实编码自二进制) - 用
Uint8Array.from(str, c => c.charCodeAt(0))构造字节数组
完整还原流程:Base64 → Uint8Array → Blob → URL
假设你拿到的是 data URL 形式的图片 Base64,如 data:image/png;base64,iVBORw0KGgo...:
const base64 = dataUrl.split(',')[1]; // 提取纯 Base64 部分
const str = atob(base64); // ⚠️ 这步只是中间字符串,不是二进制
const bytes = new Uint8Array(str.length);
for (let i = 0; i < str.length; i++) {
bytes[i] = str.charCodeAt(i); // 逐字符还原为原始字节
}
const blob = new Blob([bytes], { type: 'image/png' });
const url = URL.createObjectURL(blob);关键点:
- 务必提取
data:前缀后的 Base64 主体,否则atob会失败 -
Blob的type必须匹配原始图片类型(可从 data URL 头部解析,或由业务约定) - 如果 Base64 字符串含空格或换行(比如从某些后端返回),需先
.replace(/[\r\n\s]/g, '')
更安全的替代方案:使用 Uint8Array.from(atob(...), c => c.charCodeAt(0))
上面的手动循环可以简化为一行(ES2015+):
const bytes = Uint8Array.from(atob(base64), c => c.charCodeAt(0));
但要注意:
- 该写法在 Safari 9–10 中不支持(
Uint8Array.from第二个参数需 polyfill) - 若 Base64 长度不是 4 的倍数,
atob会抛InvalidCharacterError,需提前校验或补等号 - 对超大 Base64(如几 MB 的图),
atob在部分旧版 Chrome 中有内存限制,可能触发Out of memory
为什么不用 fetch + blob()?
如果你能控制数据来源,直接用 fetch(dataUrl) 是更健壮的方式:
fetch(dataUrl).then(res => res.blob()).then(blob => {
const url = URL.createObjectURL(blob);
});优势:
- 完全绕过
atob的字符串陷阱 - 自动处理 MIME 类型和编码
- 支持流式解析,内存更友好
但前提是 dataUrl 是合法的、浏览器支持的格式;某些环境(如 Web Worker 或严格 CSP)可能禁用 data URL 的 fetch。
真正容易被忽略的是:Base64 解码不是“解密”,它不恢复元信息(如宽高、EXIF),也不校验完整性——哪怕解出来能生成 Blob,图片也可能因传输损坏而打不开。

















