Vue2.0结合webuploader实现文件分片上传功能


Posted in Javascript onMarch 09, 2018

Vue项目中遇到了大文件分片上传的问题,之前用过webuploader,索性就把Vue2.0与webuploader结合起来使用,封装了一个vue的上传组件,使用起来也比较舒爽。

上传就上传吧,为什么搞得那么麻烦,用分片上传?

分片与并发结合,将一个大文件分割成多块,并发上传,极大地提高大文件的上传速度。

当网络问题导致传输错误时,只需要重传出错分片,而不是整个文件。另外分片传输能够更加实时的跟踪上传进度。

实现后的界面:

Vue2.0结合webuploader实现文件分片上传功能 

主要是两个文件,封装的上传组件和具体的ui页面,上传组件代码下面有列出来。这两个页面的代码放到github上了: https://github.com/shady-xia/Blog/tree/master/vue-webuploader 。

在项目中引入webuploader

1.先在系统中引入jquery(插件基于jq,坑爹啊!),如果你不知道放哪,那就放到 index.html 中。

2.在 官网 上下载 Uploader.swf webuploader.min.js ,可以放到项目静态目录 static 下面;在 index.html 中引入webuploader.min.js。

(无需单独再引入 webuploader.css ,因为没有几行css,我们可以复制到vue组件中。)
<script src="/static/lib/jquery-2.2.3.min.js"></script>
<script src="/static/lib/webuploader/webuploader.min.js"></script>

需要注意的点:

1.在vue组件中,通过 import './webuploader'; 的方式引入webuploader,会报''caller', 'callee', and 'arguments' properties may not be accessed on strict mode ...'的错, 这是因为你的babel使用了严格模式,而caller这些在严格模式下禁止使用。所以 可以直接在index.html中引入webuploader.js ,或者手动去解决babel中'use strict'的问题。

基于webuploader封装Vue组件

封装好的组件upload.vue如下,接口可以根据具体的业务进行扩展。

注意:功能和ui分离,此组建封装好了基本的功能,没有提供ui,ui在具体的页面上去实现。

<template>
 <div class="upload">
 </div>
</template>
<script>
 export default {
  name: 'vue-upload',
  props: {
   accept: {
    type: Object,
    default: null,
   },
   // 上传地址
   url: {
    type: String,
    default: '',
   },
   // 上传最大数量 默认为100
   fileNumLimit: {
    type: Number,
    default: 100,
   },
   // 大小限制 默认2M
   fileSingleSizeLimit: {
    type: Number,
    default: 2048000,
   },
   // 上传时传给后端的参数,一般为token,key等
   formData: {
    type: Object,
    default: null
   },
   // 生成formData中文件的key,下面只是个例子,具体哪种形式和后端商议
   keyGenerator: {
    type: Function,
    default(file) {
     const currentTime = new Date().getTime();
     const key = `${currentTime}.${file.name}`;
     return key;
    },
   },
   multiple: {
    type: Boolean,
    default: false,
   },
   // 上传按钮ID
   uploadButton: {
    type: String,
    default: '',
   },
  },
  data() {
   return {
    uploader: null
   };
  },
  mounted() {
   this.initWebUpload();
  },
  methods: {
   initWebUpload() {
    this.uploader = WebUploader.create({
     auto: true, // 选完文件后,是否自动上传
     swf: '/static/lib/webuploader/Uploader.swf', // swf文件路径
     server: this.url, // 文件接收服务端
     pick: {
      id: this.uploadButton,  // 选择文件的按钮
      multiple: this.multiple, // 是否多文件上传 默认false
      label: '',
     },
     accept: this.getAccept(this.accept), // 允许选择文件格式。
     threads: 3,
     fileNumLimit: this.fileNumLimit, // 限制上传个数
     //fileSingleSizeLimit: this.fileSingleSizeLimit, // 限制单个上传图片的大小
     formData: this.formData, // 上传所需参数
     chunked: true,   //分片上传
     chunkSize: 2048000, //分片大小
     duplicate: true, // 重复上传
    });
    // 当有文件被添加进队列的时候,添加到页面预览
    this.uploader.on('fileQueued', (file) => {
     this.$emit('fileChange', file);
    });
    this.uploader.on('uploadStart', (file) => {
     // 在这里可以准备好formData的数据
     //this.uploader.options.formData.key = this.keyGenerator(file);
    });
    // 文件上传过程中创建进度条实时显示。
    this.uploader.on('uploadProgress', (file, percentage) => {
     this.$emit('progress', file, percentage);
    });
    this.uploader.on('uploadSuccess', (file, response) => {
     this.$emit('success', file, response);
    });
    this.uploader.on('uploadError', (file, reason) => {
     console.error(reason);
     this.$emit('uploadError', file, reason);
    });
    this.uploader.on('error', (type) => {
     let errorMessage = '';
     if (type === 'F_EXCEED_SIZE') {
      errorMessage = `文件大小不能超过${this.fileSingleSizeLimit / (1024 * 1000)}M`;
     } else if (type === 'Q_EXCEED_NUM_LIMIT') {
      errorMessage = '文件上传已达到最大上限数';
     } else {
      errorMessage = `上传出错!请检查后重新上传!错误代码${type}`;
     }
     console.error(errorMessage);
     this.$emit('error', errorMessage);
    });
    this.uploader.on('uploadComplete', (file, response) => {
     this.$emit('complete', file, response);
    });
   },
   upload(file) {
    this.uploader.upload(file);
   },
   stop(file) {
    this.uploader.stop(file);
   },
   // 取消并中断文件上传
   cancelFile(file) {
    this.uploader.cancelFile(file);
   },
   // 在队列中移除文件
   removeFile(file, bool) {
    this.uploader.removeFile(file, bool);
   },
   getAccept(accept) {
    switch (accept) {
     case 'text':
      return {
       title: 'Texts',
       exteensions: 'doc,docx,xls,xlsx,ppt,pptx,pdf,txt',
       mimeTypes: '.doc,docx,.xls,.xlsx,.ppt,.pptx,.pdf,.txt'
      };
      break;
     case 'video':
      return {
       title: 'Videos',
       exteensions: 'mp4',
       mimeTypes: '.mp4'
      };
      break;
     case 'image':
      return {
       title: 'Images',
       exteensions: 'gif,jpg,jpeg,bmp,png',
       mimeTypes: '.gif,.jpg,.jpeg,.bmp,.png'
      };
      break;
     default: return accept
    }
   },
  },
 };
</script>
<style lang="scss">
// 直接把官方的css粘过来就行了
</style>

使用封装好的上传组件

新建页面,使用例子如下:

ui需要自己去实现。 大概的代码可以点这里 。

<vue-upload
    ref="uploader"
    url="xxxxxx"
    uploadButton="#filePicker"
    multiple
    @fileChange="fileChange"
    @progress="onProgress"
    @success="onSuccess"
></vue-upload>

分片的原理及流程

当我们上传一个大文件时,会被插件分片,ajax请求如下:

Vue2.0结合webuploader实现文件分片上传功能 

1.多个upload请求均为分片的请求,把大文件分成多个小份一次一次向服务器传递

2.分片完成后,即upload完成后,需要向服务器传递一个merge请求,让服务器将多个分片文件合成一个文件
分片

可以看到发起了多次 upload 的请求,我们来看看 upload 发送的具体参数:

Vue2.0结合webuploader实现文件分片上传功能 

第一个配置( content-disposition )中的 guid 和第二个配置中的 access_token ,是我们通过webuploader配置里的 formData ,即传递给服务器的参数

后面几个配置是文件内容,id、name、type、size等

其中 chunks 为总分片数, chunk 为当前第几个分片。图片中分别为12和9。当你看到chunk是11的upload请求时,代表这是最后一个upload请求了。

合并

分片后,文件还未整合,数据大概是下面这个样子:

Vue2.0结合webuploader实现文件分片上传功能 

做完了分片后,其实工作还没完,我们还要再发送个ajax请求给服务器,告诉他把我们上传的几个分片合并成一个完整的文件。

我怎么知道分片上传完了,我在何时做合并?

webuploader插件有一个事件是 uploadSuccess ,包含两个参数, file 和后台返回的 response ;当所有分片上传完毕,该事件会被触发,

我们可以通过服务器返回的字段来判断是否要做合并了。

比如后台返回了 needMerge ,我们看到它是 true 的时候,就可以发送合并的请求了。

Vue2.0结合webuploader实现文件分片上传功能 

存在的已知问题

在做单文件暂停与继续上传时,发现了这个插件的bug:

1、当设置的 threads>1 ,使用单文件上传功能,即stop方法传入file时,会报错 Uncaught TypeError: Cannot read property 'file' of undefined

出错的源码如下:这是因为暂停时为了让下一个文件继续传输,会将当前的pool池中pop掉暂停的文件流。这里做了循环,最后一次循环的时候,v是undefined的。

Vue2.0结合webuploader实现文件分片上传功能 

2、设置的threads为1,能正常暂停,但是暂停后再继续上传是失败的。

原理和上一个一样,暂停时把当前文件流在 pool 中全部 pop 了,当文件开始 upload 的时候,会检查当期 pool ,而此时已经没有之前暂停的文件流了。

如果是针对所有文件整体的暂停和继续,功能是正常的。

总结

以上所述是小编给大家介绍的Vue2.0结合webuploader实现文件分片上传功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
JS获取客户端IP地址、MAC和主机名的7个方法汇总
Jul 21 Javascript
JavaScript使用cookie记录临时访客信息的方法
Apr 07 Javascript
JavaScript实现网页对象拖放功能的方法
Apr 15 Javascript
js计算时间差代码【包括计算,天,时,分,秒】
Apr 26 Javascript
Highcharts 多个Y轴动态刷新数据的实现代码
May 28 Javascript
使用JS实现图片展示瀑布流效果(简单实例)
Sep 06 Javascript
JavaScript两个变量交换值的实现方法
Mar 01 Javascript
帝国cms首页列表页实现点赞功能
Oct 30 Javascript
vue中使用refs定位dom出现undefined的解决方法
Dec 21 Javascript
vue iView 上传组件之手动上传功能
Mar 16 Javascript
Vue $emit()不能触发父组件方法的原因及解决
Jul 28 Javascript
JS实现按比例缩小图片宽高
Aug 24 Javascript
vue + vuex todolist的实现示例代码
Mar 09 #Javascript
vue实现在表格里,取每行的id的方法
Mar 09 #Javascript
vue移动端UI框架实现QQ侧边菜单组件
Mar 09 #Javascript
vue的安装及element组件的安装方法
Mar 09 #Javascript
11行JS代码制作二维码生成功能
Mar 09 #Javascript
浅谈vue.js导入css库(elementUi)的方法
Mar 09 #Javascript
使用use注册Vue全局组件和全局指令的方法
Mar 08 #Javascript
You might like
PHP6 mysql连接方式说明
2009/02/09 PHP
PHP array操作10个小技巧分享
2011/06/23 PHP
PHP实现Soap通讯的方法
2014/11/03 PHP
PHP计算数组中值的和与乘积的方法(array_sum与array_product函数)
2016/04/01 PHP
PHP实现浏览器中直接输出图片的方法示例
2018/03/14 PHP
php实现生成PDF文件的方法示例【基于FPDF类库】
2018/07/21 PHP
JavaScript 撑出页面文字换行
2009/06/15 Javascript
JavaScript中函数声明与函数表达式的区别详解
2016/08/18 Javascript
jquery 属性选择器(匹配具有指定属性的元素)
2016/09/06 Javascript
简单谈谈gulp-changed插件
2017/02/21 Javascript
10 种最常见的 Javascript 错误(频率最高)
2018/02/08 Javascript
Vue 中使用vue2-highcharts实现曲线数据展示的方法
2018/03/05 Javascript
jquery操作select常见方法大全【7种情况】
2019/05/28 jQuery
vue二选一tab栏切换新做法实现
2021/01/19 Vue.js
[00:08]DOTA2勇士令状等级奖励“天外飞星”
2019/05/24 DOTA
python中使用OpenCV进行人脸检测的例子
2014/04/18 Python
Python基于PycURL自动处理cookie的方法
2015/07/25 Python
python解决Fedora解压zip时中文乱码的方法
2016/09/18 Python
使用Python对Excel进行读写操作
2017/03/30 Python
Python决策树和随机森林算法实例详解
2018/01/30 Python
Django之模型层多表操作的实现
2019/01/08 Python
利用python中集合的唯一性实现去重
2020/02/11 Python
pyecharts绘制中国2020肺炎疫情地图的实例代码
2020/02/12 Python
Python 防止死锁的方法
2020/07/29 Python
canvas需要在标签里直接定义宽高
2014/12/17 HTML / CSS
英国网络托管和域名领导者:Web Hosting UK
2017/10/15 全球购物
Pedro官网:新加坡时尚品牌
2019/08/27 全球购物
size?荷兰官方网站:英国高级运动鞋精品店
2020/07/24 全球购物
服装销售人员求职自我评价
2013/09/26 职场文书
元旦晚会主持词
2014/03/24 职场文书
党的群众路线教育实践活动领导班子整改措施
2014/09/30 职场文书
一个家长教育孩子的心得体会
2016/01/15 职场文书
英镑符号 £
2022/02/17 杂记
Pyhton爬虫知识之正则表达式详解
2022/04/01 Python
改造DE1103三步曲
2022/04/07 无线电
Win11如何启用启动修复 ? Win11执行启动修复的三种方法
2022/04/08 数码科技