🔍 为什么大文件上传是个挑战?
传统文件上传方式(简单 **<input type="file">** + FormData)在遇到大文件时会有这些问题:
- ❌ 内存溢出:浏览器可能崩溃(尤其是 1GB+ 文件)
- ❌ 网络不稳定:上传失败后需要从头开始
- ❌ 无进度反馈:用户不知道要等多久
👉 解决方案 = 分片上传 + 断点续传 + 并行传输!
📦 核心方案:分片上传(Chunk Upload)
1. 前端分片处理(使用 File API)
const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB 每片
async function chunkFile(file) {
const chunks = [];
let start = 0;
while (start < file.size) {
const end = Math.min(start + CHUNK_SIZE, file.size);
const chunk = file.slice(start, end); // Blob.slice 切割文件
chunks.push(chunk);
start = end;
}
return chunks;
}
2. 上传分片(并发控制)
async function uploadChunks(chunks, fileHash) {
const MAX_CONCURRENT = 3; // 最大并发数
const queue = [];
for (let i = 0; i < chunks.length; i++) {
const formData = new FormData();
formData.append('chunk', chunks[i]);
formData.append('hash', `${fileHash}-${i}`);
formData.append('total', chunks.length);
queue.push(
fetch('/upload', { method: 'POST', body: formData })
.then(res => res.json())
);
// 控制并发
if (queue.length >= MAX_CONCURRENT) {
await Promise.race(queue);
}
}
await Promise.all(queue); // 等待所有分片完成
}
⚡ 进阶优化:断点续传 + 秒传
1. 文件指纹生成(SparkMD5)
import SparkMD5 from 'spark-md5';
async function calculateFileHash(file) {
return new Promise((resolve) => {
const spark = new SparkMD5.ArrayBuffer();
const reader = new FileReader();
reader.onload = (e) => {
spark.append(e.target.result);
resolve(spark.end());
};
reader.readAsArrayBuffer(file);
});
}
2. 服务端检查(避免重复上传)
async function checkFileExist(fileHash) {
const res = await fetch(`/check?hash=${fileHash}`);
return res.json();
/* 返回示例:
{ exists: true, uploadedChunks: [0,1,2] }
*/
}
🛠️ 技术选型对比
💡 用户体验优化技巧
1. 进度条显示(基于 axios/fetch)
const { data } = await axios.post('/upload', formData, {
onUploadProgress: (progressEvent) => {
const percent = Math.round(
(progressEvent.loaded / progressEvent.total) * 100
);
console.log(`上传进度: ${percent}%`);
}
});
2. 拖拽上传 + 预览(Dropzone.js 示例)
<div id="dropzone">
<p>拖拽文件到此处上传</p>
<img id="preview" style="max-width: 200px;">
</div>
<script>
dropzone.addEventListener('drop', (e) => {
const file = e.dataTransfer.files[0];
const preview = document.getElementById('preview');
preview.src = URL.createObjectURL(file);
});
</script>
📈 性能对比:优化前后
🚀 扩展阅读
💬 互动话题:
你在项目中如何处理大文件上传? 遇到过哪些坑?欢迎分享! 💡
(如果觉得有用,请点赞/收藏支持!❤️)