云计算百科
云计算领域专业知识百科平台

一文搞懂“流式数据分片上传“

用户上传一段 2GB 的视频,结果上传到一半断了,用户急得骂人。有没有一种方案,能让大文件上传变得\”断不断都不怕\”。今天就把我研究出来的分片上传方案写下来,顺便把代码也贴出来,大家可以直接拿去用。


一、先搞懂一个问题:为什么要分片上传?

你有没有遇到过这种场景:

  • 上传一个 500MB 的视频,传到 99% 的时候网络抖动了一下,然后从头再来。
  • 用户心态直接崩了。

传统上传方式就是把整个文件一次性发到服务器,简单粗暴,但有个致命缺陷:一旦中断,前功尽弃。

分片上传的思路其实很简单——把大文件切成小块,一块一块传。这样即使某一块传失败了,只需要重传那一块,不用整个文件重来。

打个比方:

你要搬家,东西很多。传统方式是一辆卡车把所有家具一次拉走,路上抛锚了全部得重来。分片上传呢?叫了 10 辆小货车,每辆拉几件家具,哪辆抛锚了只修那辆,不影响其他的。


二、分片上传的完整流程

整个流程可以拆成 5 步,我尽量用大白话讲:

第 1 步:文件分片

把用户选中的大文件,按固定大小切成小块。比如每块 5MB,一个 50MB 的文件就切成 10 块。

前端用 File.prototype.slice() 方法就能切,不需要后端配合。

第 2 步:创建上传任务

在开始传之前,先跟服务器打个招呼:“我要传一个文件,请给我一个唯一的 uploadId”。这个 ID 就像快递单号,后面所有分片都要带上它,服务器靠它知道这些分片属于哪个文件。

第 3 步:并行上传分片

10 块分片不需要一块一块等,可以同时发起多个请求一起传。现代浏览器对同一个域名的并发连接数限制一般是 6 个,所以一次并发传 6 个分片,剩下的等前面的完了再继续。

第 4 步:服务端合并

所有分片都传完了,服务器收到通知,按分片序号把它们拼回去,就还原成了原始文件。

第 5 步:通知 AI 处理

文件合并完之后,如果业务需要(比如视频分析),再调用 AI 处理接口让它干活。


三、关键特性:让上传\”抗造\”的五个技能

光能传还不行,实际项目中用户的行为是不可控的。以下五个特性是必须支持的:

1. 断点续传

用户传着传着关了页面,下次再来打开,已经传完的分片不用重传,直接从断掉的地方继续。实现方式:每次传完一个分片,就把这个分片的序号存到 localStorage 里。重新上传时先查一下哪些传过了,跳过就行。

2. 并行上传

前面说了,同时发多个请求,速度起飞。但也不能无脑并发,浏览器有并发限制,一般设 3~6 个比较合理。

3. 进度显示

用户需要知道\”到底传到哪了\”。进度 = 已上传分片数 / 总分片数 × 100%。每传完一块就更新一下,用户体验拉满。

4. 错误重试

网络波动导致某个分片传失败了,不要直接报错停止,自动重试几次(比如 3 次),还是失败再告诉用户。

5. 暂停 / 恢复

用户可能突然不想传了,想暂停,等会儿再传。这个需要配合 AbortController 来实现取消请求。


四、前端代码实现

下面我把完整的分片上传类写出来,带注释,直接复制就能用(当然后端接口需要你自己配合)。

4.1 基础版:最简单的分片上传

先写一个最基础的版本,让你理解核心逻辑:

class ChunkedUploader {


constructor(options = {

}) {


this.chunkSize = options.chunkSize || 5 * 1024 * 1024; // 每块 5MB
this.maxConcurrent = options.maxConcurrent || 3; // 最大并发数
this.retryCount = options.retryCount || 3; // 重试次数
this.onProgress = options.onProgress || (() => {

}); // 进度回调
}

async upload(file, uploadId) {


const totalChunks = Math.ceil(file.size / this.chunkSize);
console.log(\\`总共 \\${

totalChunks} 个分片\\`);

// 记录每个分片的上传状态
const chunkStatus = new Array(totalChunks).fill(\’pending\’);

// 按批次并发上传
for (let i = 0; i < totalChunks; i += this.maxConcurrent) {


const batch = [];
for (let j = 0; j < this.maxConcurrent && (i + j) < totalChunks; j++) {


const index = i + j;
const start = index * this.chunkSize;
const end = Math.min(start + this.chunkSize, file.size);
const chunk = file.slice(start, end);

batch.push(
this._uploadChunk(uploadId, chunk, index).then(() => {


chunkStatus[index] = \’done\’;
this.onProgress((index + 1) / totalChunks * 100);
})
);
}
await Promise.all(batch);
}

console.log(\’所有分片上传完成\’);
}

async _uploadChunk(uploadId, chunk, index) {


let retries = 0;
while

赞(0)
未经允许不得转载:网硕互联帮助中心 » 一文搞懂“流式数据分片上传“
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!