Blob 和 File 到底有什么区别?
在前端开发中,处理二进制数据是家常便饭。无论是上传图片、下载文件、还是操作 Canvas,你都会频繁遇到 `Blob` 和 `File` 这两个 API。它们看起来很像,甚至用法也几乎一致,以至于很多开发者用了很久都没搞清楚二者本质上的差异。
Blob 和 File 到底有什么区别?
在前端开发中,处理二进制数据是家常便饭。无论是上传图片、下载文件、还是操作 Canvas,你都会频繁遇到 Blob 和 File 这两个 API。它们看起来很像,甚至用法也几乎一致,以至于很多开发者用了很久都没搞清楚二者本质上的差异。
这篇文章就来彻底讲清楚:Blob 和 File 到底是什么关系?它们的核心区别在哪里?什么时候该用哪个?
一、Blob:二进制大对象(Binary Large Object)
Blob 是浏览器内置的一个通用二进制数据容器。它代表了一段不可变的原始二进制数据,本身不包含任何关于数据的元信息(比如文件名、类型等,虽然可以指定 MIME type,但这只是提示,不是文件属性)。
基本用法
javascript// 创建一个 Blob const blob = new Blob(['Hello, World!'], { type: 'text/plain' }); console.log(blob.size); // 13(字节数) console.log(blob.type); // "text/plain"
Blob 的核心特点:
- 只读:创建后内容不可修改
- 无文件名:它只是一段数据,没有"文件"的概念
- 可切片:可以通过
slice()方法分割数据,常用于大文件分片上传 - 可作为 URL:通过
URL.createObjectURL(blob)生成临时链接,供<img>、<a>等标签使用
二、File:继承自 Blob 的文件对象
File 是 Blob 的子类。它继承了 Blob 的所有属性和方法,同时增加了文件特有的元信息。
基本用法
javascript// 通过文件输入获取 File 对象 const input = document.querySelector('input[type="file"]'); input.addEventListener('change', (e) => { const file = e.target.files[0]; // FileList 中都是 File 对象 console.log(file.name); // "avatar.png" console.log(file.size); // 1024(继承自 Blob) console.log(file.type); // "image/png"(继承自 Blob) console.log(file.lastModified); // 时间戳 });
File 在 Blob 的基础上新增了三个关键属性:
| 属性 | 说明 |
|---|---|
name | 文件名(含扩展名) |
lastModified | 文件最后修改时间的时间戳 |
webkitRelativePath | 相对路径(拖拽文件夹时有用) |
三、核心区别:一张图看懂
File 继承自 Blob
Blob: raw binary data
├── size
├── type
└── slice()
File: file-specific binary data
├── (继承 Blob 的所有属性方法)
├── name
└── lastModified
本质区别总结
| 维度 | Blob | File |
|---|---|---|
| 本质 | 纯二进制数据块 | 带有文件元信息的二进制数据 |
| 来源 | 手动创建、Ajax 响应、Canvas 导出等 | 用户通过 <input> 选择、拖拽、系统 API |
| 文件名 | ❌ 没有 | ✅ 有 name 属性 |
| 修改时间 | ❌ 没有 | ✅ 有 lastModified |
| 继承关系 | 父类 | 子类(File.prototype.__proto__ === Blob.prototype) |
四、使用场景对比
什么时候用 Blob?
1. 手动组装二进制数据
javascript// 将多个文本/二进制片段合并为一个 Blob const parts = ['Hello ', 'World']; const blob = new Blob(parts, { type: 'text/plain' });
2. Canvas 导出图片
javascriptconst canvas = document.getElementById('canvas'); canvas.toBlob((blob) => { // 这里得到的是 Blob,没有文件名 const url = URL.createObjectURL(blob); }, 'image/png');
3. 下载后端返回的二进制流
javascriptconst response = await fetch('/api/export'); const blob = await response.blob(); // 返回的是 Blob
4. 大文件分片上传
javascriptconst file = document.getElementById('file').files[0]; const chunkSize = 1024 * 1024; // 1MB for (let start = 0; start < file.size; start += chunkSize) { const chunk = file.slice(start, start + chunkSize); // slice() 返回的是 Blob(虽然 file 是 File,但 slice 返回 Blob) uploadChunk(chunk); }
什么时候用 File?
1. 文件上传场景
javascriptconst formData = new FormData(); formData.append('file', fileInput.files[0]); // 必须是 File,保留文件名
2. 需要文件元信息时
javascriptconst file = e.target.files[0]; if (file.size > 10 * 1024 * 1024) { alert('文件超过 10MB'); } if (!file.name.endsWith('.pdf')) { alert('只支持 PDF 格式'); }
3. 手动构造 File(较少见,但有用)
javascript// 将 Blob "升级" 为 File const blob = new Blob(['content'], { type: 'text/plain' }); const file = new File([blob], 'report.txt', { type: 'text/plain', lastModified: Date.now() });
五、常见误区
误区 1:fetch 返回的 response.blob() 为什么是 Blob 不是 File?
因为 HTTP 响应只是一段二进制流,它本身没有"文件名"的概念(除非 Response Header 中有 Content-Disposition)。浏览器不会擅自给你造一个 File 对象出来。
误区 2:Blob 能转成 File 吗?
可以,但严格来说不是"转",而是"基于 Blob 创建 File":
javascriptconst file = new File([blob], 'filename.ext', { type: blob.type });
注意第一个参数是数组,这是 File 构造函数的要求。
误区 3:File 的 slice 返回的是 File 还是 Blob?
返回的是 Blob。这是规范定义的:
javascriptconst file = document.getElementById('file').files[0]; const sliced = file.slice(0, 100); console.log(sliced instanceof File); // false console.log(sliced instanceof Blob); // true
这意味着分片上传时,你上传的每个 chunk 都是 Blob,丢失了原文件名——这很正常,因为服务器只需要数据块,不需要重复的文件名。
六、一句话总结
Blob 是"原材料",File 是"贴了标签的原材料"。当你只需要处理二进制数据时用 Blob;当你需要知道这份数据来自哪个文件、什么时候修改过时,用 File。
在实际开发中,你经常会遇到它们混用的情况——比如从 <input type="file"> 拿到 File,切片成 Blob 上传,后端返回 Blob,前端再包装成 File 提供给用户下载。理解它们的继承关系和本质差异,能让你在操作二进制数据时更加游刃有余。