前端上传切片优化以及实现,主要涉及到大文件分片上传(chunk upload)、并发控制、断点续传、秒传、重试机制等。
🔹 场景与痛点
- 大文件上传(>100MB):直接上传会超时或失败。
- 网络不稳定:中断后需重传。
- 上传速度慢:需要并发分片上传。
- 服务端压力大:需要合理控制并发与分片大小。
🔹 基础实现流程
-
文件切片
使用Blob.slice
方法将文件分割为固定大小的分片(比如 2MB/5MB)。1function createFileChunks(file: File, chunkSize = 2 * 1024 * 1024) { 2 const chunks: Blob[] = [] 3 let cur = 0 4 while (cur < file.size) { 5 chunks.push(file.slice(cur, cur + chunkSize)) 6 cur += chunkSize 7 } 8 return chunks 9}
-
计算文件唯一标识(hash)
通常用 MD5/SHA1 或者基于文件名 + 大小 + 上次修改时间。
可以在浏览器端用spark-md5
:1import SparkMD5 from "spark-md5"; 2 3async function calculateHash(chunks: Blob[]) { 4 const spark = new SparkMD5.ArrayBuffer(); 5 for (const chunk of chunks) { 6 const buffer = await chunk.arrayBuffer(); 7 spark.append(buffer); 8 } 9 return spark.end(); 10}
-
上传分片
每个分片通过FormData
上传:1async function uploadChunk(chunk: Blob, index: number, fileHash: string) { 2 const formData = new FormData(); 3 formData.append("chunk", chunk); 4 formData.append("index", String(index)); 5 formData.append("fileHash", fileHash); 6 7 return fetch("/upload", { 8 method: "POST", 9 body: formData, 10 }); 11}
-
合并文件
前端所有分片上传完成后,调用后端/merge
接口,通知服务端进行文件合并。
🔹 优化点
-
并发控制
使用Promise.all
并发上传,但需要限制最大并发数:1async function limitUpload(chunks, limit = 5) { 2 const pool: Promise<any>[] = [] 3 let i = 0 4 5 async function run() { 6 if (i >= chunks.length) return 7 const task = uploadChunk(chunks[i], i, "fileHash").then(run) 8 pool.push(task) 9 i++ 10 } 11 12 const workers = Array(limit).fill(null).map(run) 13 await Promise.all(workers) 14}
-
断点续传
- 上传前向服务端查询已上传的分片列表。
- 跳过已完成的分片,仅上传剩余分片。
-
秒传
- 上传前计算
hash
。 - 询问服务端该文件是否已存在,存在则直接返回成功。
- 上传前计算
-
失败重试
- 针对失败的分片,做 最多 N 次重试。
1async function retry(fn, retries = 3) { 2 while (retries--) { 3 try { 4 return await fn(); 5 } catch (e) { 6 if (!retries) throw e; 7 } 8 } 9}
-
上传进度显示
- 每个分片上传时用
XMLHttpRequest.onprogress
或fetch + ReadableStream
计算进度。 - 进度 = 已上传分片大小 / 总文件大小。
- 每个分片上传时用
🔹 前端完整流程
- 选择文件 → 切片 → 计算
hash
。 - 调用
/checkFile
→ 返回已上传分片。 - 跳过已完成分片,继续上传剩余分片(带并发控制 & 重试机制)。
- 上传完后请求
/merge
。 - 前端实时展示进度条。
🔹 技术选型
- 切片与上传:原生
Blob.slice
+fetch
/axios
。 - hash计算:
spark-md5
(大文件可用 Web Worker 避免卡 UI)。 - 断点续传:前端记录进度 / 服务端存储分片状态。
- 进度显示:
XMLHttpRequest.onprogress
或axios.onUploadProgress
。
个人笔记记录 2021 ~ 2025