vue webuploader 组件开发

最近项目中需要用到百度的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&#45;&#45;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>

四、运行效果图

大功告成。欢迎交流

posted @ 2017-09-22 17:20  <虎口脱险>  阅读(5448)  评论(20编辑  收藏  举报