踩坑Webuploader视频上传
背景
由于我司业务关系,需实现兼容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> x <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"> 已选择 个, </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错误问题,只需在弹窗显示完全时,用样式调整即可