视频上传与播放流程
文件上传步骤
前端上传
-
通过
<input type="file">
标签选择要上传的文件。 -
获取到
input.files[0]
就是选中的File
对象。 -
将
File
对象放入FormData
中,通过POST
请求发送给后端。如果文件较大,可以在前端对文件进行切片处理:
const file = input.files[0];
const chunkSize = 2 * 1024 * 1024; // 2MB
let cur = 0;
while (cur < file.size) {
// 切割文件
const chunk = file.slice(cur, cur + chunkSize);
cur += chunkSize;
// 转成blob对象
const chunkBlob = new Blob([chunk]);
// 放入FormData
formData.append('chunk', chunkBlob);
}
后端处理
-
后端接收到
FormData
数据后,会获得文件的size
、name
、type
等信息,以及文件的二进制内容。将文件内容存储到服务器的某个目录下,如backend_folder
。如果前端传来的是切片,后端需要对切片进行处理与合并:
// express为例
const multiparty = require('multiparty');
const path = require('path');
const fse = require('fs-extra');
const form = new multiparty.Form();
form.parse(req, async (err, fields, files) => {
if (err) {
return res.send({
code: 1001,
msg: '切片上传失败',
});
}
// 获取切片文件
const chunk = files.chunk[0];
// 创建临时目录
const chunkDir = path.resolve(uploadDir, `${fileName}-chunks`);
await fse.ensureDir(chunkDir);
// 把切片移动到临时目录
await fse.move(chunk.path, `${chunkDir}/${chunk.originalFilename}`);
// 合并切片文件
await mergeFileChunk(filePath, chunkDir, size);
res.send({
code: 0,
msg: '切片上传成功',
});
}); -
后端返回给前端一个可访问的文件 url,格式如
https://www.xxx.com/backend_folder/xxx.mp4
。
视频切片
- 后端将视频文件
xxx.mp4
进行切片处理,生成m3u8
索引文件和多个ts
切片文件。 m3u8
和ts
文件存放在backend_folder
下的某个目录,如static
。- 此时
m3u8
文件可通过类似https://www.xxx.com/backend_folder/static/xxx.m3u8
的 url 访问。
前端播放
- 前端拿到
m3u8
的 url 后,传给视频播放器,如xgplayer
:const player = new Player({
id: 'video',
url: 'https://www.xxx.com/backend_folder/static/xxx.m3u8',
}); - 播放器通过
m3u8
索引文件逐个加载ts
切片进行播放,实现分段加载。
大文件切片的意义
视频文件通常较大,如果整个文件一次性上传,很容易出现超时失败的情况。而且上传是无状态的,只有文件全部上传完才能知道是否上传成功。
切片上传可以把大文件分成多个小的文件块,每一块单独上传。这样即使某个切片上传失败,也只需重新上传这一个切片,而不是整个文件重传。同时还可以并发上传多个切片,提高上传速度。
切片上传为大文件的上传提供了可恢复、可断点续传的能力,是上传大文件的最佳实践之一。