Vue大文件切片上传 断点续传

对于我们每个人而言:【生于黑暗,追逐黎明————《异兽迷城》】

一、中等文件上传解决方案-nginx放行

在我们工作中,上传功能最常见的就是excel的上传功能,一般来说,一个excel的大小在10MB以内吧,如果有好几十MB的excel,就勉强算是中等文件吧,此时,我们需要设置nginx的client_max_body_size值,将其放开,只不过一次上传一个几十MB的文件,接口会慢一些,不过也勉强能够接受

二、大文件上传解决方案 -- 文件切片上传 断点续传

思路:

1、把大文件切成每块10M(按照你自己的要求),然后依次上传(为了让你好理解,你可以理解成分页,一共89条数据,每页数10条,一共9页)

2、上传完成后端把文件封装好,返回上传的url地址 加上进度条

3、中途上传中断,如果再次上传如何回到原来的位置继续上传,解决办法就是每次上传前给后端请求接口查验下,该文件上传到第几片了,和后端对接好chunk参数,比如-1代表已经上传完,0代表第0片,以此类推。

完善各种情况:比如发生错误,是否可以尝试重新上传,离开页面以后取消上传操作(离开页面以后,上传还在继续,这种情况除了上传还有计时器的问题,这里就不多说了,总之离开之前先清掉的思路)

PS:一定要上传完一个才能上传完下一个,是串行不是并行,另外,ajax一定要是异步的不管是原生还是jq的ajax,因为我开始弄成同步,progress事件根本出不来

后台需要提供三个接口

1、上传文件的接口

2、上传之前查验文件是否上传过,上传到第几片的接口

3、上传文件完成merge的接口

     具体逻辑 Vue

<template>
    <div>
      <input id="file" name="file" type="file"/>
      <el-button size="small" type="primary" id="startBtn">上传视频</el-button>
      <el-progress style="width: 400px"  v-if="percentage==100 && !isError" :percentage="percentage" status="success"></el-progress>
      <el-progress style="width: 400px" :percentage="percentage" v-else-if="percentage > 0 && !isError && percentage < 100"></el-progress>
      <el-progress style="width: 400px" :percentage="percentage" status="exception" v-else-if="isError"></el-progress>
    </div>
</template>

<script>
  export default {
    data() {
	return {
		percentage: 0, // 进度条
      	isError: false, // 是否发生错误
      	request: null  // ajax请求
	}
},
mounted() {
	var pecent;
  var start;
  var end;
	var file;
	var name;
	var size;
	var shardCount;
	var i = 0;
	var shardSize;
    var GUID;
		
        var status = 0;
        var _this = this;
		
        var page = {
            init: function(){
                $("#startBtn").click($.proxy(this.upload, this));
            },
            upload: function(){
                status = 0;
                
          
                file = $("#file")[0].files[0];  //文件对象

                if (!file) {
                  _this.$message.warning('请选择文件');
                  return;
                }
                name = file.name;        //文件名
                size = file.size;        //总大小
                GUID = this.guid(file.name, file.lastModified, file.size, file.type);
                shardSize = 10 * 1024 * 1024;   //以1MB为一个分片
                shardCount = Math.ceil(size / shardSize);  //总片数
                
                // 获取当前的片数
                let formData = new FormData();
                formData.append("md5", GUID);
                getCurrentFileChunk(formData).then(res => {
                  if (res.result.chunk < 0) {
                    _this.form.videoUrl = res.result.url;
                    _this.percentage = 100;
                  } else {
                    status = res.result.chunk;
                    start = res.result.chunk * shardSize;
                    end = Math.min(size, start + shardSize);
                    var partFile = file.slice(start,end);
                    

                    var pecent=100*(start * shardSize)/file.size;
                    _this.percentage = parseInt(pecent);

                    this.partUpload(GUID,partFile,name,shardCount,status);
                  }
                });
            },
            partUpload:function(GUID,partFile,name,chunks,chunk){
              // 重新上传的时候
              _this.isError = false;

              //构造一个表单,FormData是HTML5新增的
              var  now = this;
              var form = new FormData();
              form.append("md5", GUID);
              form.append("file", partFile);  //slice方法用于切出文件的一部分
              form.append("chunk", chunk);        //当前是第几片
              //form.append("chunks", chunks);  //总片数
              //Ajax提交
              _this.request = $.ajax({
                  url: process.env.API_F_URL + "/files/uploadVideo",
                  type: "POST",
                  data: form,
                  async: true,    //同步
                  processData: false,  //很重要,告诉jquery不要对form进行处理
                  contentType: false,  //很重要,指定为false才能形成正确的Content-Type
                  success: function(data){
                      status++;
                      if (status < chunks) {
                        start = status * shardSize,
                        end = Math.min(size, start + shardSize);
                        var partFile = file.slice(start,end);
                        now.partUpload(GUID,partFile,name,shardCount,status);
                      }
                      
                      // if(data.code == 200){
                      //   $("#output").html(status+ " / " + chunks);
                      // }
                      if(status==chunks){
                        now.mergeFile(GUID,name, chunks);
                      }
                  },
                  error: function(err) {
                    console.log('err', err);
                    if (err.statusText === 'abort') {
                       _this.$message.warning('已取消上传');
                    } else {
                      _this.isError = true;
                      _this.$message.error('上传失败,请重新上传');
                      
					  // 上传失败,再次上传
                      // start = status * shardSize,
                      // end = Math.min(size, start + shardSize);
                      // var partFile = file.slice(start,end);
                      // now.partUpload(GUID,partFile,name,shardCount,status);
                    }
                  },
                  xhr: function () {
                    //获取ajax中的ajaxSettings的xhr对象  为他的upload属性绑定progress事件的处理函数
                    var myXhr = $.ajaxSettings.xhr();
                    if (myXhr.upload) {
                      //检查其属性upload是否存在
                      myXhr.upload.addEventListener("progress", function(ev){
                        if(ev.lengthComputable){
                          pecent=100*(ev.loaded+start)/file.size;
                          if(pecent>99){
                            pecent=99;
                          }
                         
                          _this.percentage = parseInt(pecent);
                        }
                      }, false);
                    }
                    return myXhr;
                  },
                });
        },
        mergeFile:function(GUID,name,chunks){
            var formMerge = new FormData();
            formMerge.append("md5", GUID);
            formMerge.append("fileName", name);
			      formMerge.append("chunks", chunks);
            $.ajax({
                url: process.env.API_F_URL + "/files/mergeVideo",
                type: "POST",
                data: formMerge,
                processData: false,  //很重要,告诉jquery不要对form进行处理
                contentType: false,  //很重要,指定为false才能形成正确的Content-Type
                success: function(res){
                  if (res.status == 'success') {
                    _this.isError = false;
                    _this.form.videoUrl = res.result.url;
                    _this.percentage = 100;
                  } else {
                    _this.isError = true;
                    _this.$message.error('上传失败,请重新上传');
                  }
                },
                error: function(err) {
                  _this.isError = true;
                  _this.$message.error('上传失败,请重新上传');
                }
            });
        },
        guid:function(name, lastModified, size, type){
				  return md5(name+'#'+lastModified+'#'+size+'#'+type);
        }
    };
        $(function(){
            page.init();
        });
    },
    beforeDestroy() {
        this.request.abort();
    }
}
</script>

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值