有时候我们开发后台管理系统的时候,比如官网后台,需要上传很多宣传图片或者宣传视频什么的。 当视频或图片的比较大的时候,有可能经常会碰到页面卡着不动的情况,然后时间久了以后要不是报错,要不就是失败了,那么遇到这种情况该怎么办呢?
那就只能分片上传或者断点续传了,今天我们就分析分析这个方法。
一、 分片上传
分片上传的思路就是把某个整体进行拆解,然后逐个上传,等成功以后再进行拼接的一个过程
-
前端分片切割:
javascript// 假设我们有一个来自 input[type=file] 的 File 对象 const file = document.getElementById('fileInput').files[0]; const chunkSize = 5 * 1024 * 1024; // 每片 5MB (可调整) let start = 0; let chunkIndex = 0; const chunks = []; while (start < file.size) { const end = Math.min(start + chunkSize, file.size); const chunk = file.slice(start, end); // 关键API:Blob.slice() chunks.push({ index: chunkIndex, file: chunk, totalChunks: Math.ceil(file.size / chunkSize), fileName: file.name, fileSize: file.size, chunkSize: chunkSize, // 强烈建议添加唯一标识,用于后端重组和去重 fileIdentifier: generateFileHash(file) // 如计算MD5/SHA(可异步) }); start = end; chunkIndex++; }
-
分片上传 : 前端通过循环请求将每个
chunk
对象独立上传到服务器。每个请求只携带文件的一小部分。 -
后端接收与存储 : 后端接收到每个分片后,根据请求中的标识信息(如
fileIdentifier
+chunkIndex
)将其临时存储。分片通常先存磁盘或对象存储,而非直接入库。
二、 断点续传
段带你续传我们从字面意思理解就是:当正在上传过程中断了,等恢复正常以后继续从之前上传的地方开始,比如 网络闪断、用户误关页面、服务器重启... 上传中断是常态。
-
前端记录:
- 在上传每个分片前,将分片索引(
chunkIndex
)等信息持久化存储(localStorage
,IndexedDB
, 甚至服务器)。 - 上传成功后,标记该分片完成。
- 当需要恢复上传时,先读取存储的状态,找出哪些分片还没传,只上传这些缺失的分片。
- 在上传每个分片前,将分片索引(
-
服务端支持:
- 提供
/check-upload
接口:前端在开始上传(或恢复上传)前,携带fileIdentifier
询问后端哪些分片已存在。 - 后端根据
fileIdentifier
查找已存储的分片列表(如索引数组[0, 1, 3, 4]
),返回给前端。 - 前端对比已有分片列表,只上传缺失的分片(如索引
2
)。
- 提供
-
最终合并:
- 前端在检测到所有分片上传完成后,发送一个
/merge
请求给后端。 - 后端根据
fileIdentifier
,找到所有属于该文件的分片,按索引顺序拼接(concat
)成完整的原始文件。 - 合并完成后,删除临时分片文件,将完整文件移动到永久存储位置,返回最终文件地址。
- 前端在检测到所有分片上传完成后,发送一个
三、经验总结
-
分片的大小要合适:
- 当太小时:请求的数量太多了,这样的话建立的连接也多,到时候很有可能触发后端设置的频率限制。
- 当太大时:失去分片意义,单个分片上传失败代价高。
- 合适大小:1MB ~ 10MB 是常见范围。考虑实际网络环境和后端处理能力。
-
文件标识 : 必须使用文件内容生成唯一标识(如 MD5, SHA-1, SHA-256),确保前后端生成逻辑一致。
-
服务端合并:
- 文件覆盖 :如果出现多个用户上传同名文件的话,
fileIdentifier
不同,会导致文件冲突的,最终存储路径应包含fileIdentifier
或唯一 ID,而非单纯依赖原始文件名。
- 文件覆盖 :如果出现多个用户上传同名文件的话,