2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > vue实现大文件分片上传断点续传并展示上传进度条

vue实现大文件分片上传断点续传并展示上传进度条

时间:2023-01-03 01:52:08

相关推荐

vue实现大文件分片上传断点续传并展示上传进度条

最近有一个上传视频到服务器的功能,然后发现视频太大了,比如1个G的视频文件基本都是上传失败的,我之前都是上传阿里云的,所以面对大文件上传服务器就做了分片上传和断点续传。

首先解释什么是分片上传:比如一个文件是22M,我令5M为一片那么就可以分5片,一片一片上传给后端然后都上传完成开始合并这个文件。

断点续传:还是以上面的例子,有5片小文件,比如我传第一片的时侯没问题,第二片,第三片因为网络啊什么的原因打断了,第四片,第五片又上传成功了;那我下一次传相同的文件时,后端会返回给我那几片没成功,那么我只要传没成功的分片就可以,全部传完就可以调用合并文件接口。

接下来我就说说自己项目这个大文件视频是怎么进行分片断点续传的,代码里有注释,菜鸡写的不好,轻点喷

1.上传文件然后自定义上传

<el-uploadclass="upload-demo"action="#":http-request="requestUpload":show-file-list="false":before-upload="beforeUpload"drag:accept="'video/*'"v-if="file === null"><i class="el-icon-upload"></i><div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div><div class="el-upload__tip" slot="tip">只能上传视频文件</div></el-upload><div v-else><p>{{file.name }}</p><el-progress :percentage="percentage" v-if="percentage !== 0"></el-progress></div>

// 覆盖默认的上传行为requestUpload() {},//上传前的操作,可以对文件做一些限制beforeUpload(file) {if (file.type != "video/mp4") {this.$modal.msgError("视频上传只支持Mp4格式");return false;} else {this.file = file;}},

然后点击确定进行上传到服务器

/** 确定提交按钮 */submitForm() {this.$refs["form"].validate((valid) => {if (valid) {if (this.file != null) {//点击确定显示loading效果this.confirmLoading = true;//重置进度条为0this.percentage = 0;//这里走分片上传逻辑this.uploadByPieces({file: this.file, //视频实体pieceSize: 5, //分片大小success: (data) => {this.confirmLoading = false;this.percentage = 100;this.$modal.msgSuccess("新增成功");this.open = false;this.getList();},error: (e) => {this.confirmLoading = false;this.file = null;this.percentage = 0;this.$modal.msgError("分片上传视频失败");},});} else {this.$modal.msgError("请上传视频");}}});},//断点分片上传uploadByPieces({file, pieceSize = 2, success, error }) {// 上传过程中用到的变量let fileMD5 = ""; // md5加密文件的标识const chunkSize = pieceSize * 1024 * 1024; // 分片大小const chunkCount = Math.ceil(file.size / chunkSize); // 总片数//得到某一片的分片const getChunkInfo = (file, currentChunk, chunkSize) => {let start = currentChunk * chunkSize;let end = Math.min(file.size, start + chunkSize);let chunk = file.slice(start, end);return chunk;};// 第一步const readFileMD5 = () => {// 读取视频文件的md5console.log("获取文件的MD5值");//得到第一片和最后一片const startChunk = getChunkInfo(file, 0, chunkSize);const endChunk = getChunkInfo(file, chunkCount - 1, chunkSize);console.log(startChunk, endChunk);//对第一片进行转码然后md5加密,网上很多是直接对整个文件转码加密得到标识,但是我发现大文件尤其是几个G的文件会崩溃,所以我是先分片然后取第一片加密let fileRederInstance = new FileReader();fileRederInstance.readAsBinaryString(startChunk);fileRederInstance.addEventListener("load", (e) => {let fileBolb = e.target.result;fileMD5 = md5(fileBolb);//检查文件有没有上传过的状态uploadCheckAxios({identifier: fileMD5, totalChunks: chunkCount }).then((res) => {if (res.data.needMerge == true) {console.log("文件都已上传,现在需要合并");//调用合并接口,合并接口后端做了异步处理不然会超时let time = new Date().getTime();mergeFileAxios({name: this.form.name,subjectName: this.form.subjectName,teacherName: this.form.teacherName,categoryId: this.form.categoryId,description: this.form.description,identifier: fileMD5,totalSize: file.size,filename: time + "_" + file.name,}).then((res) => {console.log("文件合并成功");success && success(res);}).catch((e) => {console.log(e, "文件合并错误");error && error(e);});} else {//这里很多博客都是直接for循环,可我实际联调发现for循环不等返回,虽然上传对顺序没影响但是很多分片其实是超时了会上传失败,所以我处理成数组,从需要上传的数组第一个分片上传有了返回后就从前面删掉数组元素直到数组为[]//文件未被上传if (res.data.status === 0) {console.log("文件未被上传");//整理需要上传的分片的数组let needUploadList = [];for (let i = 0; i < chunkCount; i++) {needUploadList.push(i);}console.log(needUploadList);readChunkMD5(needUploadList);}//文件已被上传过一部分else {let arr = res.data.uploaded;//如果上传过,进度条开始的数据要计算一下let per = 100 / chunkCount;this.percentage = Number((arr.length * per).toFixed(2));console.log(this.percentage);console.log(arr);let needUploadList = [];console.log("文件已被上传过一部分");//整理需要上传的分片的数组for (let i = 0; i < chunkCount; i++) {if (!arr.includes(i)) {needUploadList.push(i);}}console.log(needUploadList);readChunkMD5(needUploadList);}}}).catch((e) => {error && error(e);});});};// 针对每个分片文件进行上传处理const readChunkMD5 = (needUploadList) => {if (needUploadList.length > 0) {let i = needUploadList[0];console.log(i);//得到当前需要上传的分片文件const chunk = getChunkInfo(file, i, chunkSize);let fetchForm = new FormData();//后端需要的参数fetchForm.append("chunkNumber", i);fetchForm.append("chunkSize", chunkSize);fetchForm.append("currentChunkSize", chunk.size);fetchForm.append("file", chunk);fetchForm.append("filename", fileMD5 + "-" + i);fetchForm.append("identifier", fileMD5);fetchForm.append("totalChunks", chunkCount);fetchForm.append("totalSize", file.size);//上传接口uploadVideoChunkAxios(fetchForm).then((res) => {console.log(res);//都上传了,等待合并if (res.data === true) {console.log("文件开始合并");let time = new Date().getTime();mergeFileAxios({name: this.form.name,subjectName: this.form.subjectName,teacherName: this.form.teacherName,categoryId: this.form.categoryId,description: this.form.description,identifier: fileMD5,totalSize: file.size,filename: time + "_" + file.name,}).then((res) => {console.log("文件合并成功");success && success(res);}).catch((e) => {console.log(e, "文件合并错误");error && error(e);});} else {//每上传一个就在进度条上加数据let per = 100 / chunkCount;console.log(per, this.percentage);let totalPrecent = Number((this.percentage + per).toFixed(2));if (totalPrecent > 100) {this.percentage === 100;} else {this.percentage = totalPrecent;}console.log(this.percentage);let newArr = JSON.parse(JSON.stringify(needUploadList));if (newArr.length > 0) {newArr.shift();readChunkMD5(newArr);}}}).catch((e) => {error && error(e);});} else {console.log("上传结束");}};readFileMD5(); // 开始执行代码},

就到这里啦,结束

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。