前端大文件切片上传、取消、显示进度

4,196 阅读1分钟
<script>
import axios from "axios";
const progressList = [];
export default {
  data() {
    return {
      complete: 0,
      totalSize: 0,
      cancelHandleList: []
    };
  },
  methods: {
    upload() {
      const formData = new FormData();
      const fileList = this.$refs.file.files;
      const self = this;
      if (!fileList.length) {
        alert("请选择文件");
        return;
      }
      for (var i = 0; i < fileList.length; i++) {
        formData.append("f1", fileList[i]); //支持多文件上传
      }
      const CancelToken = axios.CancelToken;
      axios({
        method: "post",
        url: "http://localhost:8100/",
        headers: { "Content-Type": "multipart/form-data" },
        data: formData,
        onUploadProgress: progressEvent => {
          let complete = (progressEvent.loaded / progressEvent.total) * 100;
          this.complete = complete;
        },
        cancelToken: new CancelToken(function executor(c) {
          
          // 这个参数 c 就是CancelToken构造函数里面自带的取消请求的函数,这里把该函数当参数用
        })
      })
        .then(res => {})
        .catch(err => {
          console.log(err);
        });
    },
    submitUpload() {
      const chunkSize = 1024 * 1024; // 分片大小 1M
      const file = this.$refs.file.files[0];
      this.totalSize = file.size;
      const chunks = []; // 保存分片数据
      let token = +new Date(); // 时间戳
      let name = file.name;
      let chunkCount = 0;
      let sendChunkCount = 0;

      //拆分文件 像操作字符串一样
      if (file.size > chunkSize) {
        //拆分文件
        let start = 0, end = 0;
        while (true) {
          end += chunkSize;
          const blob = file.slice(start, end);
          start += chunkSize;
          if (!blob.size) {
            //截取的数据为空 则结束
            break;
          }
          chunks.push(blob); //保存分段数据
        }
      } else {
        chunks.push(file.slice(0));
      }
      chunkCount = chunks.length; //分片的个数
      for (let i = 0; i < chunkCount; i++) {
        const fd = new FormData(); //构造FormData对象
        fd.append("token", token);
        fd.append("f1", chunks[i]);
        fd.append("index", i);
        this.send(fd, i).then(() => {
          sendChunkCount += 1;
          if (sendChunkCount === chunkCount) {
            //上传完成,发送合并请求
            console.log("上传完成,发送合并请求");
            const formD = new FormData();
            formD.append("type", "merge");
            formD.append("token", token);
            formD.append("chunkCount", chunkCount);
            formD.append("filename", name);
            this.send(formD);
          }
        })
      }
    },
    send(data, index) {
      const CancelToken = axios.CancelToken;
      return new Promise((resolve, reject) => {
        axios({
          method: "post",
          url: "http://localhost:8100/",
          headers: { "Content-Type": "multipart/form-data" },
          data: data,
          onUploadProgress: progressEvent => {
            // 保存每个分片的上传进度
            progressList[index] = progressEvent.loaded;
            // 计算已上传的总进度
            const complete = progressList.reduce((p, c) => p + c);
            this.complete = (complete / this.totalSize) * 100;
          },
          cancelToken: new CancelToken(c => {
            // 这个参数 c 就是CancelToken构造函数里面自带的取消请求的函数,这里把该函数当参数用
            // 订阅每个分片的取消事件
            this.cancelHandleList.push(c);
          })
        }).then(res => {
            resolve();
          }).catch(err => {
            console.log(err);
          });
      });
    },
    // 取消事件 依次执行分片的取消事件
    cancelHandle() {
      this.cancelHandleList.forEach(fn => fn());
    }
  }
};
</script>