前言
在前端开发中,处理图片裁剪、Excel 导出、或大文件上传时,绕不开 Blob 和 File。它们是 JavaScript 处理二进制数据的基石。本文将带你梳理这些 API 的关系,并提供一个完整的“大文件切片上传”实现思路。
一、 核心概念:Blob 与 File
1. Blob (Binary Large Object)
Blob 表示不可变的、原始的二进制数据。
- 核心方法:
slice(start, end, contentType)。 - 场景:常用于大文件分片、将 Canvas 转换为图片、处理流媒体数据。
2. File
File 继承自 Blob,它在 Blob 的基础上增加了与系统文件相关的信息。
-
常见属性:
name:文件名。size:字节大小。type:MIME 类型(如image/png)。lastModified:最后修改时间的时间戳(推荐使用,代替已废弃的lastModifiedDate)。
💡 贴士:向后端传输文件时,直接将
File对象append进FormData即可,浏览器会自动处理编码,无需手动转换。
const formData = new FormData();
formData.append("image", file); // 将 File 对象附加到 FormData
fetch("/upload", { method: "POST", body: formData });
二、 异步读取:FileReader 家族
1. FileReader (主线程异步)
用于将文件内容读入内存。
| 方法 | 读取结果 (result) | 适用场景 |
|---|---|---|
readAsDataURL | Base64 编码的字符串 | 图片预览 |
readAsText | 纯文本内容 | 读取文本文件、配置文件 |
readAsArrayBuffer | ArrayBuffer 对象 | 二进制操作(加密、MD5 计算) |
readAsBinaryString | 原始二进制字符串 | 后台转换(较少用) |
2. FileReaderSync (Worker 同步)
- 场景:仅能在 Web Worker 中使用。
- 特点:会阻塞线程直到读取完成。在后台线程中同步读取大文件,逻辑更直观,且不会卡死 UI 界面。
三、 实战:图片/文本预览示例
<!doctype html>
<html>
<body>
<div>
<div>
<input type="file" id="files-list" multiple />
<div id="output" style="width: 300px; height: 300px;overflow: auto;"></div>
<div id="progress"></div>
</div>
</div>
<script>
let filesList = document.getElementById('files-list')
filesList.addEventListener('change', (event) => {
let info = '',
output = document.getElementById('output'),
progress = document.getElementById('progress'),
files = event.target.files,
type = 'default',
reader = new FileReader()
if (/image/.test(files[0].type)) {
reader.readAsDataURL(files[0])
type = 'image'
} else {
reader.readAsText(files[0])
type = 'text'
}
reader.onerror = function () {
output.innerHTML = 'Could not read file, error code is ' + reader.error.code
}
reader.onprogress = function (event) {
if (event.lengthComputable) {
progress.innerHTML = `${event.loaded}/${event.total}`
}
}
reader.onload = function () {
let html = ''
switch (type) {
case 'image':
html = `<img src="${reader.result}">`
break
case 'text':
html = reader.result
break
}
output.innerHTML = html
}
})
</script>
</body>
<style></style>
</html>
四、 核心面试题:如何实现大文件上传?
当文件达到几百 MB 甚至几 GB 时,直接上传极易超时或因网络波动失败。
1. 实现步骤
-
切片 (Chunking) :利用
file.slice()将大文件切割成等份的Blob块。 -
生成指纹 (Hashing) :利用 Web Worker 计算文件的 MD5 值(唯一标识)。这样即便文件名改了,服务器也能识别出是同一个文件(实现秒传)。
-
并发上传:调用接口循环上传切片,通常会限制并发数(如一次只传 3-6 个)。
-
合并请求:
- 前端主动触发:所有切片传完后,发一个
merge请求。 - 后端自动感应:后端计数,发现切片到齐后自动合并。
- 前端主动触发:所有切片传完后,发一个
2. 切片逻辑代码
function createFileChunk(file, size = 1024 * 1024 * 5) { // 默认 5MB 一片
const fileChunkList = [];
let cur = 0;
while (cur < file.size) {
fileChunkList.push({
file: file.slice(cur, cur + size) // 核心:利用 Blob 的 slice
});
cur += size;
}
return fileChunkList;
}
3. 进度条的细节
上传和下载的进度监听对象不同,面试常考:
- 下载进度:
xhr.onprogress - 上传进度:
xhr.upload.onprogress
xhr.upload.onprogress = (event) => {
if (event.lengthComputable) {
const percent = (event.loaded / event.total) * 100;
console.log(`当前切片上传进度:${percent}%`);
}
};