最近公司搞了个大屏,结果客户上传了一个400M的视频,由于网速慢,直接在上传途中给超时了,然后后端用 JAVA
写的个流上传,我想想那我自己也搞个 流上传
, 然后就诞生了这个最最最基础
的版本,其他改动后续更新
本章节主要讲解下eggjs
怎么通过stream
写入视频
修改
app => config => config.default.js
// 除了upload接口用 mode:'file' , 其他都用 `stream`
config.multipart = {
mode: 'stream',
fileModeMatch: /(\/upload)$/,
fileSize: 1048576000,
whitelist: [".txt", ".png", ".jpg",'.mp4'],
};
修改
app => controller => upload.js
加入
async uploadStream() {
const stream = await this.ctx.getFileStream();
const {
filename,
fields: { chunk, chunks },
} = stream;
const day = moment(new Date()).format("YYYYMMDD");
const dir = path.join(this.config.uploadDir, day);
let buf;
let uploadDir = "";
try {
await mkdirp(dir); // 不存在就创建目录
uploadDir = path.join(dir, filename);
const streams = fs.createWriteStream(uploadDir, {
flags: "a",
});
const parts = await toArray(stream);
buf = Buffer.concat(parts);
streams.write(buf);
// 当 chunk === chunks , 证明已经文件已经上传完毕 , 可以直接写入数据库
if (chunk === chunks) {
console.log("上传数据库");
}
} catch (e) {
await sendToWormhole(stream);
ctx.cleanupRequestFiles();
throw e;
}
this.ctx.body = {
code: 200,
msg: "上传成功",
data: uploadDir.replace(/app/g, ""),
};
}
}
修改
app => router.js
, 加入
router.post('/uploadStream',_jwt, controller.upload.uploadStream);
接下来我们通过ajax
写一个接口请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<input type="file" id="file" />
<button id="upload">上传</button>
<script src="../js/jquery.js"></script>
<script>
$('#upload').on('click', function (params) {
let file = $('#file').prop('files')[0];
let uid = new Date().getTime();
let sliceSize = 10 * 1024 * 1024;
let blob = file;
const fileSize = file.size; // 文件大小
const fileName = file.name; // 文件名
const totalSlice = Math.ceil(fileSize / sliceSize);
const pormiseArr = [];
for (let i = 1; i <= totalSlice; i++) {
let chunk;
if (i == totalSlice) {
chunk = file.slice((i - 1) * sliceSize, fileSize); //切割文件
} else {
chunk = file.slice((i - 1) * sliceSize, i * sliceSize);
}
const formData = new FormData();
formData.append('size', fileSize);
formData.append('chunks', totalSlice);
formData.append('chunk', i);
formData.append('name', uid + fileName);
formData.append('file', chunk, uid + fileName);
pormiseArr.push(formData);
}
callback(pormiseArr, 0);
});
// 这里之所以采用回调,是因为如果全部通过同步的,可能会出现后端拼接报错的可能
// 这里回调其实并没有解决上传较慢的问题,但是在一定层度解决了上传太久出现超时的问题
function callback(arr, index) {
$.ajax({
url: 'http://localhost:7001/uploadStream',
type: 'POST',
data: arr[index],
processData: false, // 不处理数据
contentType: false, // 不设置内容类型
success: function () {
if (index != arr.length - 1) {
callback(arr, index + 1);
} else {
console.log('上传成功');
}
},
});
}
</script>
</body>
</html>
上传快可以,加宽带价钱==
),但是解决了上传后超时的问题chunk
和 chunks
可以判断上传进度,所以 (1)前端可以写个进度条方便用户更直观看到上传进度
(2)后端可以通过这两个字段来实现断点续传的功能
,也就是不断通过记录 chunk
来记录进度的 位置
(tips: 这可能是个不太成熟的想法
)播放流
的问题最近也在 着手
,其实 video标签
就可以实现了,如果有 token
需求可以用 xgplayer
,也不失为一种解决方法