用 Blob 实现 Base64 图片转 URL:从原理到实战

在日常开发中,我们经常会遇到需要处理图片数据的场景。比如从服务器返回的是一串 Base64 编码的图片,或者某些离线缓存的数据以 Base64 形式存储,如何将这段看似乱码的字符串变为一个可以插入页面 <img> 标签显示的资源?

本篇文章围绕一个具体实战场景展开:如何将 Base64 编码转换为 Blob 对象,再生成临时 URL,并将图片插入到页面中


📌 为什么需要从 Base64 转为 Blob?

Base64 是一种将二进制数据用可打印字符表示的方式,优点是通用、跨平台、传输方便。但它也有明显的弊端:

  • 占用空间大:Base64 编码的字符串体积比原始二进制数据大约多了 33%;
  • 浏览器直接处理效率低:对于需要展示、下载、上传的资源,更推荐用 Blob 对象来封装;
  • 与现代 API 不兼容:如 FormData、File、fetch 上传等都更适配 Blob/File 类型。

因此,将 Base64 转换为 Blob 是一种更标准、更高效的做法,特别是在需要将资源进一步操作或传输时。


🧩 原始代码 & 步骤解析

先来看一段完整的代码示例,并逐行解释它做了什么。

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>base64ToBlob</title>
</head>
<body>
<script>
    // 第一步:使用 atob 解码 base64 字符串 → 原始二进制字符串(每个字符表示一个字节)
    const base64Data = atob('iVBORw0KGgoAAAANSUhEUgAAARMAAAC3CAMAAAAGjUrGAAABgFBMVEX...');

    // 第二步:创建 Uint8Array,长度与二进制字符串相同,用于存放每个字节(0-255)
    const byteArray = new Uint8Array(base64Data.length);

    // 第三步:遍历 base64 解码后的字符串,将每个字符的 Unicode 编码作为对应字节存入数组
    for (let i = 0; i < base64Data.length; i++) {
        byteArray[i] = base64Data.charCodeAt(i);
    }

    // 第四步:构造 Blob 对象,将字节数组作为其内容,并指定 MIME 类型为 image/png
    const blob = new Blob([byteArray], { type: 'image/png' });

    // 第五步:使用 URL.createObjectURL 生成一个临时可访问的本地 URL
    const url = URL.createObjectURL(blob);

    // 第六步:创建 <img> 元素,将其 src 设置为上述 URL,实现图片展示
    const img = document.createElement('img');
    img.src = url;
    document.body.appendChild(img);
</script>
</body>
</html>

🔍 每一步都做了什么?

1. Base64 解码:atob()

ini 复制代码
const base64Data = atob(base64String);
  • atob 的作用是将 Base64 编码字符串解码为原始二进制字符串;
  • 得到的不是可读文字,而是一个"每个字符等于一个字节"的原始字符串;
  • 注意,这不是数组,也不是 Buffer,而是纯字符串表示的字节序列。

2. 构造字节数组:Uint8Array

ini 复制代码
const byteArray = new Uint8Array(base64Data.length);
  • JavaScript 的 Uint8Array 是一个定长的无符号 8 位整数数组
  • 用于存放字节数据,每项值为 0~255,正好适合处理图片这种原始二进制内容;
  • 它比字符串更高效,也符合 Blob 的数据结构要求。

3. 将字符转为字节值

css 复制代码
for (let i = 0; i < base64Data.length; i++) {
    byteArray[i] = base64Data.charCodeAt(i);
}
  • 这是将"字符串格式的字节"转换为真正数值型的字节;
  • charCodeAt() 获取当前字符对应的 Unicode 编码(对于 atob 的返回值,每个字符的编码正好就是其字节值);
  • 得到的 byteArray 才是真正可以打包成 Blob 的数据结构。

4. Blob 对象的创建

go 复制代码
const blob = new Blob([byteArray], { type: 'image/png' });
  • Blob 是 HTML5 引入的构造器,用于封装任意类型的二进制数据;
  • type 指定 MIME 类型,这样浏览器就知道这是个 PNG 图片;
  • 可将多个 ArrayBuffer、字符串、TypedArray 封装成一个 Blob。

5. Blob → URL:创建可访问链接

ini 复制代码
const url = URL.createObjectURL(blob);
  • URL.createObjectURL 会返回一个临时的本地 URL(形如 blob:http://xxx/xxxxxx);
  • 这个 URL 可以像普通图片地址一样设置在 img.src 中;
  • 它不依赖服务器、不占用资源,仅在当前页面有效。

6. 图片插入页面展示

ini 复制代码
const img = document.createElement('img');
img.src = url;
document.body.appendChild(img);
  • 这样,我们就用纯前端方式,把一个 base64 编码的图片,动态转换成了可展示的图片资源;
  • 这个过程没有上传,没有下载,所有操作都在浏览器端完成,效率极高。

🧠 延伸:Blob 的实际应用场景

除了本文中的用法,Blob 还被广泛应用于以下场景:

✅ 文件下载

ini 复制代码
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'example.png';
a.click();

可以用 Blob 构建一个临时链接,模拟文件下载,让用户一键保存数据。


✅ 上传二进制文件

php 复制代码
const formData = new FormData();
formData.append('file', blob, 'upload.png');

fetch('/upload', {
  method: 'POST',
  body: formData
});

将 Blob 当作文件上传,尤其适合画板截图、canvas 导出、音频录制等场景。


✅ 音频/视频播放

ini 复制代码
const video = document.createElement('video');
video.src = URL.createObjectURL(mediaBlob);
video.controls = true;
document.body.appendChild(video);

Blob 可以用于存储和播放录制的音视频数据,常用于直播、会议系统中。


Blob 有什么优势(为什么不直接用 Base64)?

这是一个容易忽略但很关键的问题。我们可以简单对比一下 Base64 和 Blob 各自的特点:

特性 Base64 Blob
表示方式 字符串 二进制对象(类似文件)
空间效率 占用原始大小的约 1.37 倍 原始大小
创建方式 将字节转为字符串 将字节数组或字符串直接封装
浏览器处理效率 慢,尤其是大文件 快,天然支持流式和异步操作
适合用途 小图标、内嵌样式 下载、上传、文件处理、Media流处理

✅ Blob 的优势总结如下:

  1. 节省内存和带宽:Base64 会把数据变大(大约 33%),Blob 保持原始大小。
  2. 更快的处理速度:Blob 不需要将数据再转为字符串解析,性能上更好。
  3. 可流式处理 :Blob 可用于 fetchFormDataStream 等接口,更贴近真实的文件传输。
  4. 可用于 DOM 直接操作 :像 createObjectURL 可以生成 URL,直接用于 <img><a><video> 等标签中,简化操作流程。
相关推荐
小飞悟23 分钟前
JavaScript 数组精讲:创建与遍历全解析
前端·javascript
拾光拾趣录43 分钟前
虚拟滚动 + 加载:让万级列表丝般顺滑
前端·javascript
然我1 小时前
数组的创建与遍历:从入门到精通,这些坑你踩过吗? 🧐
前端·javascript·面试
豆豆(设计前端)1 小时前
如何成为高级前端开发者:系统化成长路径。
前端·javascript·vue.js·面试·electron
今天你写算法了吗1 小时前
ScratchCard刮刮卡交互元素的实现
前端·javascript
谢尔登1 小时前
【React Native】布局和 Stack 、Slot
javascript·react native·react.js
FogLetter1 小时前
深入浅出 JavaScript 数组:从基础到高级玩法
前端·javascript
springfe01012 小时前
模块与组件区别
javascript·vue.js
十盒半价2 小时前
JS 数组进阶:从基础到实战的全方位解析
前端·javascript·trae
丘耳2 小时前
前端高频刷新、SSE/XHR请求管理与性能优化实战(笔记)
前端·javascript