欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

vue webuploader 文件上传组件开发

程序员文章站 2022-09-08 14:34:43
最近项目中需要用到百度的webuploader大文件的分片上传,对接后端的fastdfs,于是着手写了这个文件上传的小插件,步骤很简单,但是其中猜到的坑也不少,详细如下:...

最近项目中需要用到百度的webuploader大文件的分片上传,对接后端的fastdfs,于是着手写了这个文件上传的小插件,步骤很简单,但是其中猜到的坑也不少,详细如下:

一、封装组件

引入百度提供的webuploader.js、uploader.swf

css样式就直接写在组件里面了 

<template>
 <div>
  <div id="list" class="uploader-list"></div>
  <div id="wrapper">
   <div class="uploader-container">
    <div :id="id" limitsize="1" :ext="ext"></div>
    <el-button style="margin-bottom:10px;float:left;" size="small" :loading="uploadloading" type="success" @click="start">上传到服务器</el-button>
    <el-button style="margin-left: 20px;margin-bottom:10px;float:left;" :disabled="stopbtn" size="small" type="danger" @click="stop">暂停上传</el-button>
   </div>
  </div>
  <div class="el-upload__tip">{{tip}}</div>
  <div class="file-list">
   <ul class="el-upload-list el-upload-list--text">
    <li v-for="file in filelist" :class="['el-upload-list__item', 'is-' + file.status]" :key="file">
     <a class="el-upload-list__item-name">
      <i class="el-icon-document"></i>{{file.name}}
     </a>
     <label class="el-upload-list__item-status-label">
      <i :class="{'el-icon-upload-success': true,'el-icon-circle-check': listtype === 'text',
     'el-icon-check': ['picture-card', 'picture'].indexof(listtype) > -1}"></i>
     </label>
     <i class="el-icon-close" @click="removefile(file)"></i>
     <el-progress
      v-if="file.status === 'uploading'"
      :type="listtype === 'picture-card' ? 'circle' : 'line'"
      :stroke-width="listtype === 'picture-card' ? 6 : 2"
      :percentage="file.percentage">
     </el-progress>
    </li>
   </ul>
  </div>
 </div>
</template>
<script>
 import '../js/jquery.js'
 import '../js/webuploader.js'
 import { base64 } from 'js-base64'
 import cryptojs from 'crypto-js';

 export default{
  name: 'fileupload',
  props: {
   id: {
    type: string,
    default: function(){
     return "filepicker";
    }
   },
   //上传提示
   tip: {
    type: string,
    default: function(){
     return "";
    }
   },
   //文件后缀名限制
   ext: {
    type: string,
    default: function(){
     return "jpg,jpeg,png,pdf,mp4,avi.mp3";
    }
   },
   //分片大小设置
   chunksize: {
    type: number,
    default: function(){
     return 2097152;
    }
   },
   //分片上传重试次数
   chunkretry: {
    type: number,
    default: function(){
     return 1;
    }
   },
   //是否自动上传
   auto: {
    type: boolean,
    default: function(){
     return false;
    }
   },
   //上传文件大小限制
   sizelimit: {
    type: number,
    default: function(){
     return 209715200;
    }
   },
   //上传文件数量限制
   countlimit: {
    type: number,
    default: function(){
     return 5;
    }
   }
  },
  data(){
   return{
    appid: appconfig.appid,
    securitykey: appconfig.securitykey,
    checkurl: appconfig.checkurl,
    uploadurl: appconfig.uploadurl,
    mergeurl: appconfig.mergeurl,
    previewname: '选择文件',
    wul_filemd5: '',
    wul_size: 0,
    wul_filename: '',
    wul_chunk: 0,
    wul_uploader: '',
    filelist: [],
    listtype: 'text',
    percentage: 0,
    fileobject: {
     uid: '',
     name: '',
     ext: '',
     type: '',
     status: '',
     percentage: 0,
     url: ''
    },
    uploadloading: false,
    stopbtn: true
   }
  },
  methods: {
   /**
    * 获取当前上传列表中的文件
    * @returns {array|*}
    */
   getfilelist: function(){
    return this.filelist;
   },
   //绑定事件
   wul_init: function() {
    //提示只能选择一个文件
    this.wul_uploader.on('filesqueued', function (files) {
     if (files.length > 1) {
      this.$message({
       message: '请选择一张图片',
       type: 'error'
      });
      for (var i = 0; i < files.length; i++) {
       this.wul_uploader.cancelfile(files[i]);
      }
      this.wul_uploader.reset();
      this.wul_filemd5 = "";
      this.wul_size = 0;
      this.wul_filename = "";
      this.wul_chunk = 0;  //当前切片数
     }else{
      if( this.filelist.length == this.countlimit ){
       this.$message({
        message: '已经达到上传文件限制数量',
        type: 'error'
       });
      }else{
       //此时往需要上传的文件列表中添加文件
       let file = {
        uid: date.now() + this.tempindex++,
        name: files[0].name,
        type: files[0].type,
        ext: files[0].ext,
        status: "ready",
        percentage: 0
       }
       this.fileobject = file;
       this.filelist.push(this.fileobject);
      }
     }
    }.bind(this));

    //文件校验格式和大小
    this.wul_uploader.on('error', function (type) {
      debugger
     if (type == 'q_exceed_size_limit') {
      this.$message({
       message: '文件超过指定大小',
       type: 'error'
      });
     }
     if (type == 'q_type_denied') {
      this.$message({
       message: '文件格式错误,请选择文件',
       type: 'error'
      });
     }
     if (type == 'f_exceed_size') {
      this.$message({
       message: "文件超过" + this.sizelimit / 1024 / 1024 + "m",
       type: 'error'
      });
     }
    }.bind(this));

    //上传进度
    this.wul_uploader.on('uploadprogress', function (file, percentage) {
     this.percentage = percentage * 100;
     this.fileobject.status = "uploading";
     this.fileobject.percentage = this.percentage;
     console.log(this.fileobject.percentage);
    }.bind(this));

    //每次切片上传完成之后的判断
    this.wul_uploader.on('uploadaccept', function (object, ret) {
     if (ret.responsecode != 0) {
      this.wul_uploader.cancelfile(this.wul_uploader.getfiles()[0].id);
     }
    });

    this.wul_uploader.on('uploadbeforesend', function(object, data, headers) {
     console.log(data);
    });
   },

   option: function(key, val) {
    this.wul_uploader.option(key, val);
    var options = this.wul_uploader.options;
    this.wul_uploader.destroy();  //注销uploader
    this.wul_uploader = webuploader.create(options);
    this.wul_init();
   },
   start: function(){
    if(this.wul_uploader.getfiles()[0] != null) {
     this.wul_uploader.upload(this.wul_uploader.getfiles()[0].id);
     this.uploadloading = true;
     this.stopbtn = false;
    } else {
     this.$message({
      message: "请选择上传文件",
      type: 'error'
     });
    }
   },
   stop: function(){
    this.wul_uploader.cancelfile(this.wul_uploader.getfiles()[0].id);
   },
   removefile: function(file){
    this.filelist.splice(this.filelist.indexof(file), 1);
   },
   change: function(){
    this.option('accept', {
     title: 'images',
     extensions: 'gif,jpg,jpeg,bmp,png'
    });
   }
  },
  mounted(){
   webuploader.uploader.register({
    "before-send-file": "beforesendfile",
    "before-send": "beforesend",
    "after-send-file": "aftersendfile",
   }, {
    beforesendfile: function (file) {
     var deferred = webuploader.deferred();
     this.wul_uploader.md5file(file).then(function (val) {
      this.wul_filemd5 = val;
      this.wul_size = file.size;
      this.wul_filename = file.name;
      var timestamp = date.parse(new date()) / 1000;
      var signparam = "{chunksize=" + this.chunksize + ", filemd5=" + this.wul_filemd5 + ", size=" + this.wul_size + ", timestamp=" + timestamp + "}";
      var sign = base64.encode(cryptojs.hmacsha1(signparam, this.securitykey));
      // 获取断点续传位置
      jquery.ajax({
       type: "post",
       // 测试
       url: this.checkurl,
       data: {
        // 文件大小
        size: this.wul_size,
        // 文件唯一标记
        filemd5: this.wul_filemd5,
        // 切片大小
        chunksize: this.chunksize,
        // 签名
        sign: sign,
        // 应用分配id
        appid: this.appid,
        // 当前时间戳
        timestamp: timestamp

       },
       datatype: "json",
       // 上传失败
       error: function (xmlhttprequest, textstatus, errorthrown) {
        this.$message({
         message: "上传失败...",
         type: 'error'
        });
        this.uploadloading = false;
        this.stopbtn = true;
       }.bind(this),
       success: function (response) {
        if (response.responsecode == 0) { // 切片获取成功
         this.wul_chunk = response.chunk;
         deferred.resolve();
        } else { // 切片获取失败,请求成功
         this.wul_uploader.cancelfile(file);  //取消文件上传
         this.$message({
          message: "切片检查失败,请联系管理员",
          type: 'error'
         });
         deferred.resolve();
         this.uploadloading = false;
         this.stopbtn = true;
        }
       }.bind(this)
      });
      return deferred.promise();
     }.bind(this));
     return deferred.promise();
    }.bind(this),
    beforesend: function (block) {
     var deferred = webuploader.deferred();
     if (block.chunk < this.wul_chunk) {
      return deferred.reject();
     }
     this.wul_uploader.md5file(block.blob).then(function (chunkmd5) {
      var timestamp = date.parse(new date()) / 1000;
      var signparam = '{chunk=' + block.chunk + ', chunkmd5=' + chunkmd5 + ', chunksize=' + this.chunksize + ', filemd5=' + this.wul_filemd5 + ', size=' + this.wul_size + ', timestamp=' + timestamp + '}';
      var signtemp = cryptojs.hmacsha1(signparam, this.securitykey);
      var sign = base64.encode(signtemp);  //获取sign值
      this.wul_uploader.options.formdata = {
       'timestamp': timestamp,
       'appid': this.appid,
       'chunk': block.chunk,
       'chunksize': this.chunksize,
       'filemd5': this.wul_filemd5,
       'chunkmd5': chunkmd5,
       'size': this.wul_size,
       'sign': sign
      };
      deferred.resolve();
     }.bind(this))
     return deferred.promise();
    }.bind(this),
    aftersendfile: function (file) {
     var timestamp = date.parse(new date()) / 1000;
     var signparam = "{chunksize=" + this.chunksize + ", filemd5=" + this.wul_filemd5 + ", filename=" + file.name + ", size=" + this.wul_size + ", timestamp=" + timestamp + "}";
     var sign = base64.encode(cryptojs.hmacsha1(signparam, this.securitykey));
     // 如果分块上传成功,则通知后台合并分块
     jquery.ajax({
      type: "post",
      url: this.mergeurl,
      data: {
       appid: this.appid,
       filemd5: this.wul_filemd5,
       filename: file.name,
       chunksize: this.chunksize,
       sign: sign,
       size: this.wul_size,
       timestamp: timestamp
      },
      success: function (response) {
       if (response.responsecode == 0) {
        this.fileobject.status = "success";
        this.fileobject.percentage = 100;
        this.fileobject.url = response.filepath;
       } else {
        this.fileobject.status = "exception";
        this.$message({
         message: "上传失败,失败原因:" + response.responsemsg,
         type: 'error'
        });
       }
       this.uploadloading = false;
       this.stopbtn = true;
       this.wul_uploader.reset();
       this.wul_filemd5 = "";
       this.wul_size = 0;
       this.wul_filename = "";
       this.wul_chunk = 0;  //当前切片数
      }.bind(this)
     });
    }.bind(this)
   });
   this.wul_uploader = webuploader.create({
    // swf文件路径
    swf: '../js/uploader.swf',
    // 文件接收服务端。
    server: this.uploadurl,
    // 定义选择按钮
    pick: {
     "id": "#" + this.id,
     "innerhtml": this.previewname
    },
    // 自动上传
    auto: this.auto,
    // 禁止浏览器打开文件
    disableglobaldnd: true,
    // 添加截图功能
    paste: '#wrapper',
    // 定义拖动面板
    dnd: '#wrapper',
    // 分片上传
    chunked: true,
    // 分片大小为2m
    chunksize: this.chunksize,
    // 分片上传失败重试次数
    chunkretry: this.chunkretry,
    // 图片不做压缩
    compress: false,
    // 队列设置10个,为了选择多个文件的时候能提示
    filenumlimit: 10,
    // 提前准备好下一个文件
    preparenextfile: true,
    // 限制单个文件大小
    filesinglesizelimit: this.sizelimit,
    //线程数
    threads : 1,
    // 限制格式
    accept: {
     title: "access",
     extensions: this.ext
    }
   });
   this.wul_init();
  }
 }
</script>
<style>
 /* ----------------reset css--------------------- */
 html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre,
 a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp,
 small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li,
 fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td,
 article, aside, canvas, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary,
 time, mark, audio, video, input {
  margin: 0;
  padding: 0;
  border: none;
  outline: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
 }

 html, body, form, fieldset, p, div, h1, h2, h3, h4, h5, h6 {
  -webkit-text-size-adjust: none;
 }

 article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
  display: block;
 }

 body {
  font-family: arial, sans-serif;
 }

 ol, ul {
  list-style: none;
 }

 blockquote, q {
  quotes: none;
 }

 blockquote:before, blockquote:after, q:before, q:after {
  content: '';
  content: none;
 }

 ins {
  text-decoration: none;
 }

 del {
  text-decoration: line-through;
 }

 table {
  border-collapse: collapse;
  border-spacing: 0;
 }

 /* ------------ */
 #wrapper {
  width: 100%;
  margin: 0 auto;
  height: 35px;
 }

 .img-preview {
  width: 160px;
  height: 90px;
  margin-top: 1em;
  border: 1px solid #ccc;
 }

 .cropper-wraper {
  position: relative;
 }

 .upload-btn {
  background: #ffffff;
  border: 1px solid #cfcfcf;
  color: #565656;
  padding: 10px 18px;
  display: inline-block;
  border-radius: 3px;
  margin-left: 10px;
  cursor: pointer;
  font-size: 14px;

  position: absolute;
  right: 1em;
  bottom: 2em;
 }
 .upload-btn:hover {
  background: #f0f0f0;
 }
 .uploader-container{
  width: 100%;
  font-size: 10px;
 }

 .webuploader-container {
  position: relative;
  width: 100px;
  height: 21px;
  float: left;
 }
 .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: 6px 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;
 }
 .file-list{
  width: 100%;
 }
</style> 

二、导出组件

var fileupload = require('./src/file_upload.vue');

module.exports = {
 fileupload
} 

三、demo  引用方式

<template>
 <div>
  <el-card class="box-card">
   <fileupload ref="fileupload" :ext="ext" :countlimit="5" :tip="tip">aaa</fileupload>
  </el-card>
 </div>
</template>
<script>
 import {fileupload} from '@/components/fileupload/index.js'
 export default{
  name: 'hello',
  components: {fileupload},
  data(){
   return{
    filelist: [],
    ext: 'png,jpg,jpeg,mp3,mp4,pdf',
    tip: '可上传png/jpg/jpeg/mp3/mp4/pdf,大小不超过200m'
   }
  },
  created(){

  },
  methods: {
   getfilelist: function(){
    this.filelist = this.$refs.fileupload.getfilelist();
    console.log(this.filelist);
   }
  }
 }
</script> 

四、运行效果图

vue webuploader 文件上传组件开发

vue webuploader 文件上传组件开发

vue webuploader 文件上传组件开发

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。