一直觉得这些知识点混乱,核心是没理清「数据载体(File/Blob)→ 读取 / 转换工具(FileReader/URL)→ 数据格式(Base64/Blob URL)」的逻辑关系。下面从概念定义、核心 API、使用场景、实操代码四个维度,彻底理清这些知识点。
一、核心概念先理清(避免基础混乱)
先记住一条核心链路:用户选择文件(input/file)→ 得到File对象(继承Blob)→ 用FileReader/URL API处理 → 输出Base64/Blob URL格式 → 用于预览/上传
| 概念 | 本质 / 定义 | 核心特征 |
|---|---|---|
| File | 文件对象,继承自 Blob,代表用户选择的本地文件(如图片 / 文档) | 包含文件名、大小、类型、最后修改时间等元信息 |
| Blob | 二进制大对象(Binary Large Object),是存储二进制数据的基础对象 | 仅存二进制数据,无文件元信息(File 是带元信息的 Blob) |
| FileReader | 读取 Blob/File 内容的工具 API(异步) | 能将二进制数据转为 Base64 / 文本 / ArrayBuffer |
| URL API | 包含createObjectURL/revokeObjectURL,生成 Blob 的临时 URL(Blob URL) |
高效预览大文件,无需读取全部内容 |
| Base64 | 二进制数据的文本编码格式(将二进制转成 64 个可打印字符) | 文本格式,可嵌入 URL/JSON,但体积比原文件大 30% |
| Blob URL | 浏览器为 Blob/File 生成的临时本地 URL(格式:blob:http://xxx) |
仅本地有效,轻量,适合预览大文件 |
二、核心 API 详解
1. File 对象(获取用户文件)
(1)定义
File对象是用户通过<input type="file">、拖拽等方式选择的文件,继承自 Blob ,因此拥有 Blob 的所有属性(如size/type),还新增了文件元信息:
name:文件名(如avatar.png)lastModified:最后修改时间戳lastModifiedDate:最后修改日期(已废弃,用lastModified转 Date)
(2)获取方式(核心场景)
场景 1:通过 input/file 获取单个 / 多个文件
js
<input type="file" id="fileInput" multiple>
<script>
const fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', (e) => {
// e.target.files是FileList(类数组),存储选中的File对象
const files = e.target.files;
if (files.length === 0) return;
// 遍历FileList,获取每个File对象
for (let i = 0; i < files.length; i++) {
const file = files[i];
console.log('文件名:', file.name);
console.log('文件大小:', (file.size / 1024).toFixed(2) + 'KB');
console.log('文件类型:', file.type); // 如image/png、video/mp4
console.log('是否是Blob:', file instanceof Blob); // true(File继承Blob)
}
});
</script>
场景 2:拖拽获取文件
js
<div id="dropArea" style="width: 300px; height: 200px; border: 2px dashed #ccc;">
拖拽文件到这里
</div>
<script>
const dropArea = document.getElementById('dropArea');
// 阻止默认拖拽行为(否则浏览器会直接打开文件)
['dragover', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, (e) => e.preventDefault());
});
dropArea.addEventListener('drop', (e) => {
const files = e.dataTransfer.files; // 同样是FileList
if (files.length) {
const file = files[0];
console.log('拖拽的文件:', file.name);
}
});
</script>
(3)核心使用场景
- 作为 FileReader/URL API 的输入源(读取 / 预览文件);
- 直接通过 FormData 上传到后端(无需转换格式)。
2. Blob 对象(二进制数据容器)
(1)定义
Blob 是「二进制大对象」,是前端存储二进制数据的基础容器,可以理解为:「不管是文件、图片、视频,本质都是二进制数据,Blob 就是装这些数据的盒子」。
(2)核心属性
size:二进制数据的大小(字节);type:MIME 类型(如image/jpeg、text/plain)。
(3)创建方式(核心场景)
场景 1:从 File 对象获取 Blob(最常用) 因为 File 继承 Blob,所以 File 对象本身就是 Blob,可直接使用:
js
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
const blob = file; // File === 带元信息的Blob
console.log(blob.size, blob.type);
});
场景 2:手动创建 Blob(如文本转 Blob)
语法:new Blob(blobParts, options)
blobParts:数组,包含要放入 Blob 的内容(字符串、ArrayBuffer、Blob 等);options:可选,配置type(MIME)和endings(行结束符)。
js
// 示例1:文本转Blob
const text = 'Hello World';
const textBlob = new Blob([text], { type: 'text/plain' });
console.log(textBlob.size); // 11(字符串长度)
console.log(textBlob.type); // text/plain
// 示例2:拼接多个数据为Blob
const part1 = new Blob(['前端'], { type: 'text/plain' });
const part2 = new Blob(['开发'], { type: 'text/plain' });
const combinedBlob = new Blob([part1, part2], { type: 'text/plain' });
场景 3:切割 Blob(Blob.slice) 用于分片上传大文件(如 1GB 文件切成多个 100MB 的 Blob):
js
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
const chunkSize = 1024 * 1024 * 100; // 100MB/片
const start = 0;
const end = chunkSize;
// slice(start, end, contentType):切割Blob
const chunkBlob = file.slice(start, end, file.type);
console.log('分片大小:', (chunkBlob.size / 1024 / 1024).toFixed(2) + 'MB');
});
(4)核心使用场景
- 作为 FileReader 的输入源,读取为 Base64 / 文本;
- 作为 URL.createObjectURL 的输入源,生成 Blob URL 预览;
- 分片上传大文件(slice 方法);
- 前端生成文件(如将文本 / JSON 保存为本地文件)。
3. FileReader(读取 Blob/File 内容的工具)
(1)定义
FileReader 是异步读取 Blob/File 内容的 API(注意:是 "读取",不是 "修改"),可以把二进制的 Blob/File,转换成「人类可读的格式」(Base64、文本、ArrayBuffer)。
(2)核心方法(按使用频率排序)
| 方法 | 作用 | 适用场景 |
|---|---|---|
readAsDataURL(blob) |
读取为 Base64 编码的 DataURL | 图片 / 小文件预览 |
readAsText(blob) |
读取为字符串(默认 UTF-8 编码) | 文本 / JSON 文件读取 |
readAsArrayBuffer() |
读取为 ArrayBuffer(原始二进制数据) | 处理二进制(如文件加密) |
abort() |
中断读取 | 取消文件读取操作 |
(3)核心事件(异步读取的回调)
onload:读取成功完成;onerror:读取失败;onprogress:读取进度(可做上传进度条)。
(4)使用示例(核心场景)
场景 1:图片文件转 Base64(预览小图片)
js
<input type="file" id="imgInput" accept="image/*">
<img id="previewImg" style="width: 200px; margin-top: 10px;">
<script>
const imgInput = document.getElementById('imgInput');
const previewImg = document.getElementById('previewImg');
imgInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file || !file.type.startsWith('image/')) return;
// 1. 创建FileReader实例
const reader = new FileReader();
// 2. 监听读取成功事件
reader.onload = (e) => {
// e.target.result是Base64字符串(DataURL格式:data:image/png;base64,xxxx)
previewImg.src = e.target.result;
console.log('Base64结果:', e.target.result);
};
// 3. 监听读取失败
reader.onerror = () => {
alert('文件读取失败');
};
// 4. 开始读取文件为Base64
reader.readAsDataURL(file);
});
</script>
场景 2:读取文本 / JSON 文件内容
js
<input type="file" id="textInput" accept=".txt,.json">
<pre id="textPreview"></pre>
<script>
const textInput = document.getElementById('textInput');
const textPreview = document.getElementById('textPreview');
textInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (e) => {
let content = e.target.result;
// 如果是JSON文件,格式化展示
if (file.name.endsWith('.json')) {
content = JSON.stringify(JSON.parse(content), null, 2);
}
textPreview.innerText = content;
};
// 读取为文本
reader.readAsText(file);
});
</script>
场景 3:监听读取进度(大文件)
js
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
const reader = new FileReader();
// 监听进度(e.loaded已读取字节,e.total总字节)
reader.onprogress = (e) => {
const progress = (e.loaded / e.total) * 100;
console.log(`读取进度:${progress.toFixed(2)}%`);
};
reader.onload = () => {
console.log('读取完成');
};
reader.readAsDataURL(file);
});
(5)注意事项
- FileReader 是异步的,不能用同步方式获取结果(必须等 onload);
- 读取大文件(如 1GB 视频)时,用 FileReader 会卡顿(推荐用 Blob URL 替代);
- 读取完成后,若不再需要 reader,建议置为 null 释放内存。
4. URL API(生成 Blob 临时 URL)
(1)核心方法
| 方法 | 作用 |
|---|---|
URL.createObjectURL(blob) |
为 Blob/File 生成临时的 Blob URL(格式:blob:http://xxx) |
URL.revokeObjectURL(blobURL) |
释放 Blob URL(避免内存泄漏) |
(2)使用场景(核心:高效预览大文件)
场景 1:预览视频 / 音频(大文件优先用这个,比 Base64 快)
js
<input type="file" id="mediaInput" accept="video/*,audio/*">
<video id="videoPreview" controls style="width: 300px; margin-top: 10px;"></video>
<audio id="audioPreview" controls style="margin-top: 10px;"></audio>
<script>
const mediaInput = document.getElementById('mediaInput');
const videoPreview = document.getElementById('videoPreview');
const audioPreview = document.getElementById('audioPreview');
mediaInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
// 1. 生成Blob URL
const blobURL = URL.createObjectURL(file);
// 2. 预览(视频/音频直接用Blob URL作为src)
if (file.type.startsWith('video/')) {
videoPreview.src = blobURL;
audioPreview.src = '';
} else if (file.type.startsWith('audio/')) {
audioPreview.src = blobURL;
videoPreview.src = '';
}
// 3. 页面卸载/文件更换时,释放Blob URL(关键!避免内存泄漏)
mediaInput.addEventListener('change', () => {
URL.revokeObjectURL(blobURL);
});
window.addEventListener('unload', () => {
URL.revokeObjectURL(blobURL);
});
});
</script>
场景 2:预览图片(小图片 Base64,大图片 Blob URL)
js
<input type="file" id="bigImgInput" accept="image/*">
<img id="bigImgPreview" style="width: 300px; margin-top: 10px;">
<script>
const bigImgInput = document.getElementById('bigImgInput');
const bigImgPreview = document.getElementById('bigImgPreview');
bigImgInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
// 判断文件大小:>5MB用Blob URL,否则用Base64
const maxSize = 5 * 1024 * 1024; // 5MB
if (file.size > maxSize) {
const blobURL = URL.createObjectURL(file);
bigImgPreview.src = blobURL;
// 释放URL
bigImgInput.addEventListener('change', () => URL.revokeObjectURL(blobURL));
} else {
const reader = new FileReader();
reader.onload = (e) => {
bigImgPreview.src = e.target.result;
};
reader.readAsDataURL(file);
}
});
</script>
(3)Blob URL vs Base64(关键区别)
| 维度 | Blob URL | Base64 |
|---|---|---|
| 格式 | blob:http://xxx(本地临时 URL) |
data:image/png;base64,xxxx(文本) |
| 体积 | 无额外体积(直接指向二进制) | 比原文件大 30% 左右 |
| 加载速度 | 快(无需解析文本) | 慢(需解析 Base64 编码) |
| 内存占用 | 低(仅生成 URL,不读取全文件) | 高(读取全文件转文本) |
| 使用场景 | 大文件预览(视频 / 音频 / 大图片) | 小图片预览、嵌入 JSON / 本地存储 |
| 有效期 | 浏览器关闭 / 手动 revoke 后失效 | 永久有效(文本格式) |
5. Base64(二进制的文本编码)
(1)定义
Base64 是一种「将二进制数据转换成 64 个可打印字符(A-Z、a-z、0-9、+、/)」的编码方式,核心目的是:让二进制数据能通过 "只支持文本" 的场景传输 / 存储(如 JSON、URL、Cookie)。
(2)核心特征
-
编码后体积比原二进制大 30%(因为 1 个字节的二进制转成 1.3 个字节的文本);
-
格式:
data:[<mediatype>];base64,<data>(DataURL 格式),其中:mediatype:MIME 类型(如 image/png);<data>:Base64 编码后的文本。
(3)生成 / 解析方式
生成 Base64(两种方式)
① FileReader.readAsDataURL(最常用):
js
const reader = new FileReader();
reader.onload = (e) => {
const base64 = e.target.result; // 完整的DataURL格式
// 提取纯Base64(去掉前缀)
const pureBase64 = base64.split(',')[1];
console.log('纯Base64:', pureBase64);
};
reader.readAsDataURL(file);
② 手动转换(Blob→ArrayBuffer→Base64):
js
async function blobToBase64(blob) {
const arrayBuffer = await new Response(blob).arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
let base64 = '';
// 遍历Uint8Array转Base64(简化版,实际推荐用btoa)
base64 = btoa(String.fromCharCode(...uint8Array));
return `data:${blob.type};base64,${base64}`;
}
// 使用
blobToBase64(file).then(base64 => console.log(base64));
解析 Base64 为 Blob(前端还原文件)
场景:后端返回 Base64 图片,前端转成 Blob/File 用于预览 / 上传:
js
function base64ToBlob(base64) {
// 1. 拆分前缀和纯Base64
const [prefix, pureBase64] = base64.split(',');
// 2. 解析MIME类型
const mimeType = prefix.match(/data:(.*?);/)[1];
// 3. Base64转二进制
const byteCharacters = atob(pureBase64);
const byteArrays = [];
for (let i = 0; i < byteCharacters.length; i++) {
byteArrays.push(byteCharacters.charCodeAt(i));
}
const uint8Array = new Uint8Array(byteArrays);
// 4. 生成Blob
return new Blob([uint8Array], { type: mimeType });
}
// 使用示例:Base64转Blob并预览
const base64Str = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...';
const blob = base64ToBlob(base64Str);
const blobURL = URL.createObjectURL(blob);
const img = document.createElement('img');
img.src = blobURL;
document.body.appendChild(img);
(4)核心使用场景
- 小图片嵌入 HTML/CSS(如
<img src="data:image/png;base64,xxx">); - 接口传输二进制数据(如后端只接收 JSON,需把图片转 Base64 放入 JSON);
- 本地存储(如 localStorage 存小图片,因为 localStorage 只支持文本);
- 注意:大文件禁止用 Base64(体积大 + 解析慢)。
三、实战综合案例(串联所有知识点)
需求:实现「选择图片→预览(自动选 Blob URL/Base64)→ 转 Blob→ 上传到后端」的完整流程。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>文件API综合案例</title>
<style>
.container { max-width: 500px; margin: 20px auto; }
#preview { width: 100%; margin: 10px 0; }
#uploadBtn { padding: 8px 16px; background: #409eff; color: #fff; border: none; border-radius: 4px; cursor: pointer; }
</style>
</head>
<body>
<div class="container">
<input type="file" id="fileInput" accept="image/*">
<img id="preview" alt="预览图">
<button id="uploadBtn" disabled>上传</button>
</div>
<script>
const fileInput = document.getElementById('fileInput');
const preview = document.getElementById('preview');
const uploadBtn = document.getElementById('uploadBtn');
let currentFile = null; // 存储选中的File对象
// 1. 选择文件后处理
fileInput.addEventListener('change', async (e) => {
currentFile = e.target.files[0];
if (!currentFile) return;
// 2. 预览(自动选择方式)
const maxSize = 5 * 1024 * 1024; // 5MB
if (currentFile.size > maxSize) {
// 大图片:Blob URL预览
const blobURL = URL.createObjectURL(currentFile);
preview.src = blobURL;
// 释放URL
fileInput.addEventListener('change', () => URL.revokeObjectURL(blobURL));
} else {
// 小图片:Base64预览
const base64 = await blobToBase64(currentFile);
preview.src = base64;
}
uploadBtn.disabled = false;
});
// 3. 上传文件(转Blob后通过FormData上传)
uploadBtn.addEventListener('click', async () => {
if (!currentFile) return;
// File就是Blob,直接放入FormData
const formData = new FormData();
formData.append('avatar', currentFile); // key: avatar,value: Blob/File
try {
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
alert('上传成功');
} else {
alert('上传失败');
}
} catch (err) {
console.error('上传错误:', err);
alert('上传出错');
}
});
// 工具函数:Blob转Base64
function blobToBase64(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => resolve(e.target.result);
reader.onerror = (err) => reject(err);
reader.readAsDataURL(blob);
});
}
</script>
</body>
</html>
四、常见误区 & 避坑指南
-
误区 1:认为 File 和 Blob 是两个完全无关的对象
✅ 纠正:File 继承 Blob,File = Blob + 文件元信息(name/lastModified)。
-
误区 2:预览所有文件都用 Base64
✅ 纠正:大文件(视频 / 音频 / 大图片)用 Blob URL,小图片用 Base64。
-
误区 3:忘记释放 Blob URL
✅ 纠正:使用
URL.revokeObjectURL释放,否则会导致浏览器内存泄漏。 -
误区 4:Base64 可以直接上传到后端
✅ 纠正:后端接收 Base64 需要额外解析,推荐优先用 FormData 上传 Blob/File(原生二进制,体积更小)。
-
误区 5:FileReader 是同步的
✅ 纠正:FileReader 是异步的,必须在
onload事件中获取结果。
最后
记住核心链路:用户选文件(File)→ 作为Blob处理 → 预览(大文件Blob URL/小文件Base64)→ 上传(FormData传Blob/File)