async/await实现视频分片上传的核心是分片切割、有序/并发上传、服务端合并;需用Blob.slice()分片避免内存溢出,控制顺序与重试,保持UI响应。

用 async/await 实现视频分片上传,核心是把大文件切分成小块、按序(或并发)上传、最后通知服务端合并。关键在于控制分片顺序、处理失败重试、避免内存溢出,并保持 UI 可响应。
1. 分片切割:用 Blob.slice() 拆分视频文件
不要一次性读取整个视频到内存(尤其手机端易崩溃),而是用 Blob.slice() 按字节范围提取分片:
function createChunks(file, chunkSize = 2 * 1024 * 1024) { // 2MB/片
const chunks = [];
for (let start = 0; start < file.size; start += chunkSize) {
const end = Math.min(start + chunkSize, file.size);
chunks.push(file.slice(start, end));
}
return chunks;
}注意:Blob.slice() 是浅拷贝,不复制实际数据,性能好、内存友好。
2. 单片上传:封装带重试的 async 函数
每片上传独立,失败可重试,用 async/await 让逻辑线性清晰:
立即学习“Java免费学习笔记(深入)”;
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
async function uploadChunk(chunk, index, uploadId, retry = 3) {
const formData = new FormData();
formData.append('chunk', chunk);
formData.append('index', index);
formData.append('uploadId', uploadId);
<p>for (let i = 0; i <= retry; i++) {
try {
const res = await fetch('/api/upload/chunk', {
method: 'POST',
body: formData,
});
if (!res.ok) throw new Error(<code>HTTP ${res.status}</code>);
return await res.json(); // 如返回 { success: true }
} catch (err) {
if (i === retry) throw err;
await new Promise(r => setTimeout(r, 1000 * (i + 1))); // 指数退避
}
}
}3. 控制上传流程:串行 or 并发?推荐有限并发
全串行太慢,全并发可能压垮浏览器或服务端。建议用 Promise.allSettled + 并发数限制:
- 定义最大并发数(如 3~5),避免卡顿和请求被拒
- 用队列方式分批执行,每批上传完再启下一批
- 实时更新进度(如
uploadedCount / totalChunks)
async function uploadAllChunks(chunks, uploadId) {
const concurrency = 3;
const results = [];
<p>for (let i = 0; i < chunks.length; i += concurrency) {
const batch = chunks.slice(i, i + concurrency);
const batchPromises = batch.map((chunk, idx) =>
uploadChunk(chunk, i + idx, uploadId)
);
const batchResults = await Promise.allSettled(batchPromises);
results.push(...batchResults);</p><pre class="brush:php;toolbar:false;"><pre class="brush:php;toolbar:false;">// 可选:更新 UI 进度
const uploaded = i + batch.length;
console.log(`已上传 ${uploaded}/${chunks.length} 片`);}
const failed = results.filter(r => r.status === 'rejected');
if (failed.length > 0) throw new Error(共 ${failed.length} 片上传失败);
return { uploadId, total: chunks.length }; }
4. 合并与校验:上传完成后调用 merge 接口
所有分片成功后,主动通知服务端合并并校验完整性(如传 MD5 或 SHA-1):
async function mergeVideo(uploadId, fileName, fileSize) {
const res = await fetch('/api/upload/merge', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
uploadId,
fileName,
fileSize,
// 可选:前端预计算的文件 hash(需 FileReader + SparkMD5 等)
}),
});
if (!res.ok) throw new Error('合并失败');
return res.json(); // 返回最终视频 URL 或信息
}注意:服务端必须校验分片数量、顺序、大小总和,防止缺片或乱序。
不复杂但容易忽略:上传前建议先查服务端是否已存在同名文件(秒传)、支持断点续传(服务端记录已收分片)、前端加取消信号(AbortController)。这些都能基于 async/await 自然扩展。

















