vue分片上传

uploader.vue:

<template>
  <div id="page">

    <div class="main">
      <uploader ref="uploader" :options="options" :file-status-text="fileStatusText" :autoStart="false" @file-added="onFileAdded" @file-progress="onFileProgress" @file-success="onFileSuccess" @file-error="onFileError" class="uploader">
        <uploader-unsupport></uploader-unsupport>
        <uploader-drop>
          <uploader-btn class="upfile"><i class="iconfont icon-upload"></i> 上传文件</uploader-btn>
        </uploader-drop>
        <uploader-list></uploader-list>
      </uploader>
    </div>

  </div>
</template>

<script>
import axios from 'axios';
import SparkMD5 from 'spark-md5'
import { Merge } from "@/api/api.js"
export default {
  data() {
    return {
      fileMD5: '',
      identifier: '',
      options: {
        target: this.$TF.baseURL + 'api/file/UploadChunk',//
        testChunks: false,
        chunkSize: 2097152,  //2MB
        simultaneousUploads: 1, //并发上传数
        headers: {},
        query: {
          identifier: ''
        },
        maxChunkRetries: 1, //最大自动失败重试上传次数
        parseTimeRemaining: function (timeRemaining, parsedTimeRemaining) { //格式化时间
          return parsedTimeRemaining
            .replace(/\syears?/, '年')
            .replace(/\days?/, '天')
            .replace(/\shours?/, '小时')
            .replace(/\sminutes?/, '分钟')
            .replace(/\sseconds?/, '秒')
        }
      },
      statusTextMap: {
        success: '上传成功',
        error: '上传出错了',
        uploading: '上传中...',
        paused: '暂停',
        waiting: '等待中...',
        cmd5: '计算md5...'
      },
      fileStatusText: (status, response) => {
        return this.statusTextMap[status];
      },
    }
  },
  created() {
    this.options.headers.User = this.$TF.upHeaders()
    //const uploaderInstance = this.$refs.uploader.uploader;
  },
  mounted() {
    this.$nextTick(() => {
      let u = document.querySelector('.uploader-btn')
      let ipt = u.querySelector('input')
      ipt.setAttribute('accept', '.xls,.xlsx')
    })
  },
  methods: {
    onFileAdded(file) {
      file.pause();
      let fileSize = 1 * 1024 * 1024 * 1024
      // 计算MD5  
      
      if (fileSize < file.size) {
        this.$message({ message: '文件不能大于1G', type: 'error' })
        return
      }

      this.computeMD5(file);
    },
    chkMd5(file) {
      let time = new Date().getTime();
      let fileReader = new FileReader();
      let spark = new SparkMD5(); //创建md5对象(基于SparkMD5)
      fileReader.readAsBinaryString(file.file);
      //文件读取完毕之后的处理
      fileReader.onload = (e) => {
        spark.appendBinary(e.target.result);
        let md5 = spark.end();
        // console.log(`MD5计算完成:${file.name} \nMD5:${md5} \n用时:${new Date().getTime() - time} ms`);
        spark.destroy();
      };
    },
    //计算MD5
    computeMD5(file) {
      let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
        chunkSize = 2097152,
        chunks = Math.ceil(file.size / chunkSize),
        currentChunk = 0,
        spark = new SparkMD5.ArrayBuffer(),
        fileReader = new FileReader();
      let time = new Date().getTime();
      // console.log('计算MD5...')
      file.cmd5 = true;

      fileReader.onload = (e) => {
        spark.append(e.target.result);   // Append array buffer
        currentChunk++;

        if (currentChunk < chunks) {
          // let md5 = spark.end();
          // this.options.query.md5 = md5
          // console.log(`第${currentChunk}分片解析完成,md5:${md5}, 开始第${currentChunk + 1} / ${chunks}分片解析`)

          // spark.destroy(); //释放缓存
          // file.uniqueIdentifier = md5; //将文件md5赋值给文件唯一标识
          //      console.log(file,md5);
          // file.cmd5 = false; //取消计算md5状态
          //  file.resume(); //开始上传
          // let percent = Math.floor(currentChunk / chunks * 100);
          // console.log(percent);
          // file.cmd5progress = percent;
          loadNext();
        } else {
          // console.log('finished loading');
          let md5 = spark.end();
          this.fileMD5 = md5
          this.identifier = this.$TF.guid()
          this.options.query.identifier = this.identifier
          spark.destroy(); //释放缓存
          // file.uniqueIdentifier = md5; //将文件md5赋值给文件唯一标识
          file.cmd5 = false; //取消计算md5状态
          file.resume(); //开始上传
          // console.log(file);
        }
      };
      fileReader.onerror = () => {
        file.cancel();
      };

      let loadNext = () => {
        let start = currentChunk * chunkSize,
          end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;

        fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
      };

      loadNext();
    },
    // 文件进度的回调
    onFileProgress(rootFile, file, chunk) {
      // console.log(`上传中 ${file.name},chunk:${chunk.startByte / 1024 / 1024} ~ ${chunk.endByte / 1024 / 1024}`)
    },
    onFileSuccess(rootFile, file, response, chunk) {
      let resp = JSON.parse(response);
      if (resp.state == 0) {
        let obj = {
          fileName: file.name,
          identifier: this.identifier,
          totalSize: file.size,
          totalChunks: chunk.offset + 1,
          md5: this.fileMD5
        }
        Merge(obj).then((res) => {
          if (res.data.state == 0) {
            this.$emit('func', res.data)
          } else {

          }
        });
      } else {

      }
    },
    onFileError(rootFile, file, response, chunk) {
    },
  },

}
</script>

<style scoped lang="scss">
.main {
  max- 1000px;

  background: #fff;
  padding: 10px;
  h2 {
    padding: 30px 0;
    text-align: center;
    font-size: 20px;
  }
}
.uploader {
   100%;
  padding: 15px;
  font-size: 14px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
  box-sizing: border-box;
  .uploader-btn {
    margin-right: 4px;
    color: #fff;
    padding: 6px 16px;
  }
  .upfile {
    border: 1px solid #409eff;
    background: #409eff;
  }
  .updir {
    border: 1px solid #67c23a;
    background: #67c23a;
  }
  .uploader-list {
    max-height: 440px;
    overflow: auto;
    overflow-x: hidden;
    overflow-y: auto;

    height: 356px;
    /deep/.iconfont {
      font-size: 18px;
      color: #409eff;
    }
    .no-file {
      text-align: center;
      font-size: 14px;
      padding-top: 50px;
      color: #ccc;
    }
  }
}
//手机等小屏幕手持设备。当设备宽度  在  320px和768px之间时,执行当前的css
@media only screen and (min- 320px) and (max- 768px) {
  .uploader {
     98%;
    padding: 0;
    box-shadow: none;
  }
}
</style>
原文地址:https://www.cnblogs.com/Fancy1486450630/p/15703274.html