基于WebUploader实现大文件分片上传

前言

前面叙述了如何利用vue-simple-uploader实现分片上传、断点续传和秒传;
本篇学习基于WebUploader实现这些功能

使用

1.在项目中引入webuploader

注意事项
1、插件基于jquery,
2、不支持npm导入(通过 import './webuploader'; 的方式引入webuploader,会报''caller', 'callee', and 'arguments' properties may not be accessed on strict mode ...'的错,)

我们在项目index.html中进行静态引入资源
官网 上下载
Uploader.swf 和 webuploader.min.js,放到静态static文件下

在这里插入图片描述

注意事项
如遇到报错:Uncaught SyntaxError: Unexpected token ‘<‘,
是vue静态资源引入错误:https://blog.csdn.net/weixin_43909743/article/details/118804322
  1. 封装上传组件upload.vue
    封装好的组件upload.vue如下,接口可以根据具体的业务进行扩展。
<template>
   <div class="upload">
   </div>
</template>

<script>
 import { getToken } from "@/utils/auth"; // headers里面必须传递的token
   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,  // 重复上传
                   headers: { Authorization: "Bearer " + getToken() },
               });
               // 当有文件被添加进队列的时候,添加到页面预览
               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">
   .webuploader-container {
       position: relative;
   }
   .webuploader-element-invisible {
       position: absolute !important;
       clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
       clip: rect(1px,1px,1px,1px);
   }
   .webuploader-pick {
       position: relative;
       display: inline-block;
       cursor: pointer;
       background: #00b7ee;
       padding: 10px 15px;
       color: #fff;
       text-align: center;
       border-radius: 3px;
       overflow: hidden;
   }
   .webuploader-pick-hover {
       background: #00a2d4;
   }
   .webuploader-pick-disable {
       opacity: 0.6;
       pointer-events:none;
   }
</style>
  1. 使用组件
	<el-form-item v-show="form.struct == 'single'&&form.courseType == 2" label="视频文件">
      <div class="page">
          <div id="filePicker">选择文件</div>
          <div class="file-panel">
              <div class="file-list">
                  <ul class="file-item" :class="`file-${file.id}`" v-for="file in fileList1" v-show="!form.videoUrl">
                      <li class="file-type" :icon="fileCategory(file.ext)"></li>
                      <li class="file-name">{{file.name}}</li>
                      <li class="file-size">{{fileSize(file.size)}}</li>
                      <li class="file-status">上传中...</li>
                      <li class="progress"></li>
                      <li @click="remove1(file)">关闭</li>
                  </ul>
                  <div class="no-file" v-if="!fileList1.length&&!form.videoUrl"><i class="iconfont icon-empty-file"></i> 暂无待上传文件</div>
                  <div class="no-file" v-if="form.videoUrl">
                    <i class="iconfont icon-empty-file"></i> {{form.videoUrl}}
                    <button @click="form.videoUrl=''">删除</button>
                  </div>
              </div>
          </div>

          <vue-upload
                  :url="url1"
                  ref="uploader"
                  uploadButton="#filePicker"
                  @fileChange="fileChange"
                  @progress="onProgress"
                  @success="onSuccess"
          ></vue-upload>
      </div>
    </el-form-item>

data数据

fileList1: [],
url1:process.env.VUE_APP_BASE_API + "/file?type=1",
computed: {
     uploader() {
         return this.$refs.uploader;
     }
 },

methods:

fileChange(file) {
    if(this.fileList1.length){return this.$message.warning('一次只能上传一个文件')}
           if (!file.size) return;
           this.form.videoUrl=''
           this.fileList1.push(file);
           console.log(file);
   },
   onProgress(file, percent) {
       $(`.file-${file.id} .progress`).css('width', percent * 99 + '%');
       $(`.file-${file.id} .file-status`).html((percent * 99).toFixed(2) + '%');
   },
   onSuccess (file, response) {
       console.log('上传成功', response);
       this.fileList1=[]
       $(`.file-${file.id} .file-status`).html( '100%');
       this.form.videoUrl = response.data.url
   },
    resume(file) {
       this.uploader.upload(file);
    },
    stop(file) {
        this.uploader.stop(file);
    },
    remove1(file) {
        // 取消并中断文件上传
        this.uploader.cancelFile(file);
        // 在队列中移除文件
        this.uploader.removeFile(file, true);
        // 在ui上移除
        let index = this.fileList1.findIndex(ele => ele.id === file.id);
        this.fileList1.splice(index, 1);
    },
    fileSize(size) {
        return WebUploader.Base.formatSize(size);
    },
    fileCategory(ext) {
        let type = '';
        const typeMap = {
            image: ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp'],
            video: ['mp4', 'm3u8', 'rmvb', 'avi', 'swf', '3gp', 'mkv', 'flv'],
            text: ['doc', 'txt', 'docx', 'pages', 'epub', 'pdf', 'numbers', 'csv', 'xls', 'xlsx', 'keynote', 'ppt', 'pptx']
        };
        Object.keys(typeMap).forEach((_type) => {
            const extensions = typeMap[_type];
            if (extensions.indexOf(ext) > -1) {
                type = _type
            }
        });
        return type
    }, 

css

<style lang="scss">
    $h-row: 50px;
    .file-panel {
        width: 100%;
        margin-top: 10px;
        box-shadow: 0 2px 12px 1px rgba(0, 0, 0, 0.1);
        > h2 {
            height: 40px;
            line-height: 40px;
            padding: 0 10px;
            border-radius: 4px 4px 0 0;
            border-bottom: 1px solid #ccc;
            background-color: #fff;
        }
        .file-list {
            position: relative;
            height: 100px;
            overflow-y: auto;
            background-color: rgb(250, 250, 250);
        }
        .file-item {
            position: relative;
            height: $h-row;
            line-height: $h-row;
            padding: 0 10px;
            border-bottom: 1px solid #ccc;
            background-color: #fff;
            z-index: 1;
            > li {
                display: inline-block;
            }
        }
        .file-type {
            width: 24px;
            height: 24px;
            vertical-align: -5px;
        }
        .file-name {
            width: 40%;
            margin-left: 10px;
        }
        .file-size {
            width: 20%;
        }
        .file-status {
            width: 20%;
        }
        .file-operate {
            width: 10%;
            > a {
                padding: 10px 5px;
                cursor: pointer;
                color: #666;
                &:hover {
                    color: #ff4081;
                }
            }
        }
        .file-type[icon=text] {
            // background: url(../../assets/images/icon/text-icon.png);
        }
        .file-type[icon=video] {
            // background: url(../../assets/images/icon/video-icon.png);
        }
        .file-type[icon=image] {
            // background: url(../../assets/images/icon/image-icon.png);
        }
        .progress {
            position: absolute;
            top: 0;
            left: 0;
            height: $h-row - 1;
            width: 0;
            background-color: #E2EDFE;
            z-index: -1;
        }
        .no-file {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            font-size: 16px;
        }
    }
</style>
  1. 结果图
    在这里插入图片描述

  2. 参考:

后端参考:https://glory.blog.csdn.net/article/details/114284285
参考链接:https://www.jb51.net/article/136113.htm
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值