美文网首页
前端大文件分片上传(Vue)

前端大文件分片上传(Vue)

作者: symY_Y | 来源:发表于2022-04-14 15:16 被阅读0次

list.vue

<template>
  <el-button
    type="primary"
    :disabled="disabled || isDisabled"
    :loading="buttonLoading"
    @click="confirm()"
    >{{ buttonLoading ? percentage + "%" : title }}</el-button
  >
</template>

<script>
import { uploadByPieces } from "./comUpload";
export default {
  props: {
    title: {
      type: String,
      default: "保存",
    },
    disabled: {
      type: Boolean,
      default: true,
    },
    uploadSuccess: {
      type: Boolean,
      default: true,
    },
    selectFile: {
      type: Object,
      default: () => {},
    },
    url: {
      type: Function,
      default: null,
    },
    // 数据校验
    checkMsg: {
      type: Function,
      default: null,
    },
    // 数据保存
    submitMsg: {
      type: Function,
      default: null,
    },
    submitMsgStep: {
      type: Function,
      default: null,
    },
  },
  data() {
    return {
      buttonLoading: false,
      isDisabled: false,
      percentage: 0,
    };
  },
  created() {},
  watch: {
    uploadSuccess: {
      handler(val) {
        if (val) {
          this.buttonLoading = false;
          this.isDisabled = false;
        }
      },
      deep: true, // 深度监听
      immediate: true, // 初次监听即执行
    },
  },
  mounted() {},
  methods: {
   async confirm() {
      if (this.checkMsg && !await this.checkMsg()) {
        return false;
      }
      this.buttonLoading = true;
      this.isDisabled = true;
      const _self = this;
      const maxSize = 200 * 1024 * 1024;
      //大于200M文件使用分片上传
      if (this.selectFile.size >= maxSize) {
        this.upLoadData = this.selectFile.raw;
        // 分片上传
        uploadByPieces({
          file: this.upLoadData, // 文件实体
          pieceSize: 20, // 分片大小
          baseURL: this.url,   //接口地址
          selectedSize: 1024, // 用于抽取文件首尾各多少字节作为md5值
          success:async (response) => {
            this.percentage = 100;
            if (response.chunk === response.chunkCount) {
              this.$emit("uploadComplete", response);
              if (await this.submitMsg(response.filePath, response.chunkFileName)) {
                this.buttonLoading = false;
                this.isDisabled = false;
              } else {
                this.buttonLoading = false;
                this.isDisabled = false;
              }
            }
          },
          uploading: (res) => {
            //分片传输中
            this.percentage = parseInt((res.current / res.total) * 100);
          },
          error: (e) => {
            this.buttonLoading = false;
            this.isDisabled = false;
            this.$message({
              message: "上传失败!",
              type: "success",
            });
          },
        });
      } else {
        this.submitMsgStep();
      }
    },
  },
};
</script>

comUpload.js

import SparkMD5 from 'spark-md5';
//文件分片处理
export const uploadByPieces = ({
    file,
    pieceSize = 1,
    baseURL,
    selectedSize,
    success,
    uploading,
    error
}) => {
    const randomNumber = Date.now() + String(Math.round(Math.random() * 1000000))  // 生成随机数
    // 上传过程中用到的变量
    const chunkSize = pieceSize * 1024 * 1024; // pieceSize:5 MB一片
    const chunkCount = Math.ceil(file.size / chunkSize); // 总片数
    let identifier = ''
    // 计算md5的值
    const countMd5 = async () => {
        return new Promise(function async(resolve, reject) {
            const fileReader = new FileReader()
            if (file.size > selectedSize) {
                // 文件字节大于分割字节
                const md5 = new SparkMD5();
                let index = 0;
                const loadFile = (start, end) => {
                    const slice = file.slice(start, end);
                    fileReader.readAsBinaryString(slice);
                }
                loadFile(0, selectedSize);
                fileReader.onload = e => {
                    md5.appendBinary(e.target.result);
                    if (index === 0) {
                        index += selectedSize;
                        loadFile(file.size - selectedSize, file.size);
                    } else {
                        resolve(md5.end())
                    }
                };
            } else {
                fileReader.readAsBinaryString(file);
                fileReader.onload = e => {
                    resolve(SparkMD5.hashBinary(e.target.result))
                }
            }
        })
    }
    // 获取file分片
    const getChunkInfo = (file, currentChunk, chunkSize) => {
        let start = (currentChunk - 1) * chunkSize;
        let end = Math.min(file.size, start + chunkSize);
        let chunk = file.slice(start, end);
        return { start, end, chunk };
    };
    // 调用接口上传文件分片
    const uploadChunk = chunkInfo => {
        let fetchForm = new FormData();
        fetchForm.append("taskId", randomNumber); //传输任务ID
        fetchForm.append("chunkNumber", chunkInfo.currentChunk);  //第几分片
        fetchForm.append("chunkSize", chunkSize); //分块的大小
        fetchForm.append("totalChunks", chunkInfo.chunkCount);   //分片总数
        fetchForm.append("identifier", identifier); // 文件唯一标识 md5
        fetchForm.append("selectedSize", selectedSize); // 用于抽取文件首尾各多少字节作为md5值
        fetchForm.append("originalFileName", file.name); // 用于抽取文件首尾各多少字节作为md5值    
        fetchForm.append("file", chunkInfo.chunk);// 分块文件传输对象
        baseURL(fetchForm).then((res) => {
            if (~~res.success) {
                if (chunkInfo.currentChunk < chunkInfo.chunkCount) {
                    const { chunk } = getChunkInfo(
                        file,
                        chunkInfo.currentChunk + 1,
                        chunkSize
                    );
                    uploadChunk({
                        chunk,
                        currentChunk: chunkInfo.currentChunk + 1,
                        chunkCount: chunkInfo.chunkCount
                    });
                    uploading && uploading({
                        current: chunkInfo.currentChunk,
                        total: chunkInfo.chunkCount
                    });
                } else {
                    // 当总数大于等于分片个数的时候
                    if (chunkInfo.currentChunk >= chunkInfo.chunkCount - 1) {
                        res.chunk = chunkInfo.currentChunk;
                        res.chunkCount = chunkInfo.chunkCount;
                        success && success(res);
                    }
                }
            } else {
                error && error(res);
            }
        })
    };
    // 针对每个文件进行chunk处理
    const readChunk = () => {
        // 针对单个文件进行chunk上传
        const { chunk } = getChunkInfo(file, 1, chunkSize);
        uploadChunk({ chunk, currentChunk: 1, chunkCount });
    };
    countMd5().then(function (result) {
        identifier = result
        readChunk(); // 开始执行代码
    })
};

前端git项目
后端git地址
链接地址

相关文章

网友评论

      本文标题:前端大文件分片上传(Vue)

      本文链接:https://www.haomeiwen.com/subject/lvbtertx.html