Kailiming Blog

Blob 和 File 到底有什么区别?

前端
JavaScripe

在前端开发中,处理二进制数据是家常便饭。无论是上传图片、下载文件、还是操作 Canvas,你都会频繁遇到 `Blob` 和 `File` 这两个 API。它们看起来很像,甚至用法也几乎一致,以至于很多开发者用了很久都没搞清楚二者本质上的差异。

Blob 和 File 到底有什么区别?

在前端开发中,处理二进制数据是家常便饭。无论是上传图片、下载文件、还是操作 Canvas,你都会频繁遇到 BlobFile 这两个 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 的文件对象

FileBlob子类。它继承了 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); // 时间戳
});

FileBlob 的基础上新增了三个关键属性:

属性说明
name文件名(含扩展名)
lastModified文件最后修改时间的时间戳
webkitRelativePath相对路径(拖拽文件夹时有用)

三、核心区别:一张图看懂

File 继承自 Blob

Blob:  raw binary data
       ├── size
       ├── type
       └── slice()

File:  file-specific binary data
       ├── (继承 Blob 的所有属性方法)
       ├── name
       └── lastModified

本质区别总结

维度BlobFile
本质纯二进制数据块带有文件元信息的二进制数据
来源手动创建、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 导出图片

javascript
const canvas = document.getElementById('canvas');
canvas.toBlob((blob) => {
  // 这里得到的是 Blob,没有文件名
  const url = URL.createObjectURL(blob);
}, 'image/png');

3. 下载后端返回的二进制流

javascript
const response = await fetch('/api/export');
const blob = await response.blob(); // 返回的是 Blob

4. 大文件分片上传

javascript
const 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. 文件上传场景

javascript
const formData = new FormData();
formData.append('file', fileInput.files[0]); // 必须是 File,保留文件名

2. 需要文件元信息时

javascript
const 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":

javascript
const file = new File([blob], 'filename.ext', { type: blob.type });

注意第一个参数是数组,这是 File 构造函数的要求。

误区 3:File 的 slice 返回的是 File 还是 Blob?

返回的是 Blob。这是规范定义的:

javascript
const 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 提供给用户下载。理解它们的继承关系和本质差异,能让你在操作二进制数据时更加游刃有余。

Blob 和 File 到底有什么区别? | Kailiming Blog