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 相关文章推荐
正则表达式判断是否存在中文和全角字符和判断包含中文字符串长度
Sep 27 Javascript
基于Jquery的仿照flash放大图片效果代码
Mar 16 Javascript
Prototype源码浅析 Number部分
Jan 16 Javascript
getAsDataURL在Firefox7.0下无法预览本地图片的解决方法
Nov 15 Javascript
将list转换为json失败的原因
Dec 17 Javascript
jQuery中contents()方法用法实例
Jan 08 Javascript
javascript显式类型转换实例分析
Apr 25 Javascript
JS给swf传参数的实现方法
Sep 13 Javascript
Angular多选、全选、批量选择操作实例代码
Mar 10 Javascript
vue与TypeScript集成配置最简教程(推荐)
Oct 17 Javascript
node.js使用redis储存session的方法
Sep 26 Javascript
vscode配置vue下的es6规范自动格式化详解
Mar 20 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
PHP实例分享判断客户端是否使用代理服务器及其匿名级别
2014/06/04 PHP
PHP模块memcached使用指南
2014/12/08 PHP
分享PHP函数实现数字与文字分页代码
2015/07/28 PHP
破解Session cookie的方法
2006/07/28 Javascript
javascript 支持ie和firefox杰奇翻页函数
2008/07/22 Javascript
Extjs学习笔记之八 继承和事件基础
2010/01/08 Javascript
扩展js对象数组的OrderByAsc和OrderByDesc方法实现思路
2013/05/17 Javascript
js中生成map对象的方法
2014/01/09 Javascript
JS中数组Array的用法示例介绍
2014/02/20 Javascript
详解AngularJS中的依赖注入机制
2015/06/17 Javascript
AngularJS入门教程中SQL实例详解
2016/07/27 Javascript
JS实现获取当前URL和来源URL的方法
2016/08/24 Javascript
半个小时学json(json传递示例)
2016/12/25 Javascript
微信小程序 详解页面跳转与返回并回传数据
2017/02/13 Javascript
jQuery is not defined 错误原因与解决方法小结
2017/03/19 Javascript
深入理解react 组件类型及使用场景
2019/03/07 Javascript
详解Vue 换肤方案验证
2019/08/28 Javascript
ES10的13个新特性示例(小结)
2019/09/23 Javascript
iview实现图片上传功能
2020/06/29 Javascript
Vue 列表页带参数进详情页的操作(router-link)
2020/11/13 Javascript
jQuery是用来干什么的 jquery其实就是一个js框架
2021/02/04 jQuery
Python中装饰器的一个妙用
2015/02/08 Python
详解Python如何获取列表(List)的中位数
2016/08/12 Python
K-means聚类算法介绍与利用python实现的代码示例
2017/11/13 Python
在VS Code上搭建Python开发环境的方法
2018/04/06 Python
Python爬虫框架scrapy实现downloader_middleware设置proxy代理功能示例
2018/08/04 Python
python读取文件名并改名字的实例
2019/01/07 Python
Python使用ctypes调用C/C++的方法
2019/01/29 Python
Python实现的登录验证系统完整案例【基于搭建的MVC框架】
2019/04/12 Python
M1芯片安装python3.9.1的实现
2021/02/02 Python
基于canvas使用贝塞尔曲线平滑拟合折线段的方法
2018/01/10 HTML / CSS
应届生自我鉴定
2013/12/11 职场文书
校园网站的创业计划书范文
2013/12/30 职场文书
安全横幅标语
2014/06/09 职场文书
城管执法人员纪律作风整顿思想汇报
2014/09/13 职场文书
Nginx+Tomcat实现负载均衡、动静分离的原理解析
2021/03/31 Servers