踩坑Webuploader视频上传

6,008 阅读2分钟

背景

由于我司业务关系,需实现兼容IE8+浏览器的视频上传功能,且支持多选断点上传。故借助Baidu WebFE(FEX)团队开发的webuploader文件上传插件实现此业务功能。

文件上传的一般联调步骤

1.js端认证(主要是判断视频是否已存在,若已存在,返回视频已上传的文件大小,只需要判断一次):

  • 第一步:计算视频文件的md5,视频文件越大、计算md5的时间越久

  • 第二步:获取视频文件的总大小

  • 第三步:把视频名称、文件总大小、视频文件的md5字符串传输到tokenUrl

2.返回json的参数:

  • success:验证是否成功

  • fileMd5:视频文件md5

  • start:已上传的大小(没上传过的返回0,已上传部分则返回对应已上传的大小值,例如:30000字节,已上传20000字节,则返回20000)

3.验证通过后,js端上传(视频文件大的,分割的次数也越多)

  • 第一步:根据视频文件的总大小、设定每次分割视频流的大小
  • 第二步:把视频文件的md5字符串、
  • 视频已上传的大小值start(第一次是根据JS端认证的填写,第二次是根据上一次返回的填写)、分割的视频流传输到upUrl
  • 返回json的参数:
  • success:验证上传该段视频流是否成功
  • start:已上传的大小

插件配置解析

HTML部分

首先准备dom结构,包含存放文件信息的容器、选择按钮和上传按钮三个部分。

<!--视频列表显示-->
<ul class="resources_body_modal_add_list">
{#list queuedList as item}
<li class="resources_body_modal_add_list_item">
  <div class="resources_body_modal_add_list_item_hd" style="background-image: url({item.gvdPic})"></div> 
  <div class="resources_body_modal_add_list_item_bd">
    <input type="text" name="" placeholder="此处为视频名称,点击可修改" class="form-control" value={item.name} on-blur={this.updateName(item_index, $event)} />
  </div>
  <span class="resources_body_modal_add_list_item_icon" on-click={this.deleteQueuedFile(item_index)}>x</span> 
  <div class="resources_body_modal_add_list_item_bar">
    <div class="resources_body_modal_add_list_item_bar_progress"></div>
  </div>
</li> 
{/list}
</ul>
<!--文件上传按钮-->
<div class="resources_body_modal_add_field">
<div class="resources_body_modal_add_field_cell">
    <!--配置所用id-->
  <div id="filePicker" class="resources_body_modal_add_field_cell_icon" r-hide={queuedList.length == queuedLimit}>+</div>
  <p class="g-mb20" r-hide={queuedList.length == queuedLimit}>点击上传视频</p>
  <p r-hide={queuedList.length} class="js-empty">每次最多上传{queuedLimit}个,单个视频不超过4G</p>
  <p r-hide={!queuedList.length} class="js-full">
    已选择
    <span class="js-uploadLength"></span>个,
    <span class="js-uploadLeft"></span>
  </p>
</div> 
</div> 

初始化webuploader

uploader = WebUploader.create({
    resize: false, // 不压缩image     
    swf: base_URL + 'jslib/WebUploader/Uploader.swf', // swf文件路径
    method: "post",
    sendAsBinary : true,//文件上传二进制流
    fileNumLimit: 10,//验证文件总数量, 超出则不允许加入队列
    fileSingleSizeLimit: 4*1024*1024*1024,//验证单个文件大小是否超出限制, 超出则不允许加入队列
    server: upload_URL,// 文件接收服务端
    pick: {
        id: '#filePicker', //这个id是你要点击上传文件按钮的外层div的id  
        multiple : true //是否可以批量上传,true可以同时选择多个文件  
    },    
    chunked: true,//是否要分片处理大文件上传
    threads: true, //上传并发数
    chunkSize:3*1024*1024, //分片上传,每片2M,默认是5M
    prepareNextFile: true,//上传当前分片时预处理下一分片 
    //auto: false //选择文件后是否自动上传
    chunkRetry : 1, //如果某个分片由于网络问题出错,允许自动重传次数
    duplicate: false, //重复选择
    // runtimeOrder: 'html5, flash'
    accept: {
        extensions: 'avi,wmv,rm,rmvb,mov,mkv,flv,mp4,f4v,3gp,ts,wma,wav,aac',
        mimeTypes: '.avi,.wmv,.rm,.rmvb,.mov,.mkv,.flv,.mp4,.f4v,.3gp,.ts,.wma,.wav,.aac'
    }//视频文件后缀
});

监听分块上传过程中的三个时间点

WebUploader.Uploader.register({
    "before-send-file":"beforeSendFile",  
    "before-send":"beforeSend",  
    "after-send-file":"afterSendFile",  
},{  
    //时间点1:所有分块进行上传之前调用此函数  
    beforeSendFile:function(file){
        var deferred = WebUploader.Deferred();  
        //1、计算文件的唯一标记,用于断点续传  
        (new WebUploader.Uploader()).md5File(file,0,2*1024*1024)  
            .progress(function(percentage){  
            })  
            .then(function(val){  
                fileMd5=val;  
                //获取文件信息后进入下一步  
                deferred.resolve();  
            });  
        return deferred.promise();  
    },  
    //时间点2:如果有分块上传,则每个分块上传之前调用此函数  
    beforeSend:function(block){
        var deferred = WebUploader.Deferred();
        if(videoAdd[block.file.id] > block.start){
            // 分块存在,跳过    
            deferred.reject();    
        }else{    
            // 分块不存在或不完整,重新发送该分块内容    
            this.owner.options.formData={
              start: block.start,//设置视频上传的start点
              fileMd5: videoMd5[block.file.id]//设置视频上传的唯一标识MD5
            }

            deferred.resolve();    
        } 
        return deferred.promise();  
    },  
    //时间点3:所有分块上传成功后调用此函数  
    afterSendFile:function(file, response){
        //分块上传成功,执行成功回调
        successHandler(file, response, fileArr);
    
    }  
});  

显示用户选择

监听fileQueued事件来实现

uploader.on( 'fileQueued', function( file ) {
    // 符合条件的视频才会加进队列,包括大小,后缀,数量限制
    //设置文件loading状态
    uploader.md5File(file)
    // 及时显示进度
        .progress(function(percentage) {
            //可根据percentage显示获取文件信息时的进度
        })
        //完成
        .then(function(md5Val){
        //开始执行上传操作
        })
})

显示文件上传进度

//显示文件上传进度
uploader.on( 'uploadProgress', function(file, percentage) {
  var $uploading= $('.js-uploading-'+file.id);
  var progressWid= (percentage*100)+'%';
  $uploading.find('.js-bar').width(progressWid);
});

文件上传失败处理

//文件上传失败处理
uploader.on( 'uploadError', function(file, reason) {
  $('.js-uploading-'+file.id).addClass('hidden');
  uploader.cancelFile(file);//清空队列占位
});

选择文件错误处理

uploader.on('error', errorHandler);

至此视频上传功能,基本能实现。

tips:

  • 弹出窗显示上传按钮功能时,需注意初始化的时间以及销毁 webuploader 实例,应当在modal显示后,以及隐藏后回调处理。
  • 当实现多选文件上传时,需匹配好上传分块时的参数
  • IE8浏览器弹窗显示文件选择按钮可能会出现flash错误问题,只需在弹窗显示完全时,用样式调整即可