Vue implementa la carga fragmentada de archivos grandes, incluida la reanudación del punto de interrupción y la barra de progreso de carga

Primero explique qué es la carga de varias partes

        La carga parcial consiste en dividir un archivo grande en varias partes y transmitirlas parte por parte. Esto tiene la ventaja de reducir la sobrecarga de volver a cargar. Por ejemplo: si el archivo que cargamos es un archivo grande, el tiempo de carga debe ser relativamente largo, junto con la influencia de varios factores de inestabilidad de la red, es fácil interrumpir la transmisión y el usuario no tiene otra opción. pero para volver a cargar el archivo, pero podemos usar la carga de varias partes para resolver este problema. A través de la tecnología de carga de fragmentos, si la transmisión de la red se interrumpe, solo necesitamos cargar los fragmentos restantes para volver a seleccionar el archivo. No es necesario retransmitir el archivo completo, lo que reduce en gran medida la sobrecarga de la retransmisión.

Pero, ¿cómo elegimos un fragmento adecuado? Así que tenemos que considerar las siguientes cosas:

1. Cuanto más pequeño sea el fragmento, más solicitudes debe haber y mayor la sobrecarga. Por lo tanto, no se puede configurar demasiado pequeño.

2. Cuanto más grande es el fragmento, menos flexibilidad.

3. El servidor tendrá un búfer de recepción de tamaño fijo. El tamaño del segmento es preferentemente un múltiplo entero de este valor.

Pasos para la carga de varias partes

1. Primero cifre el archivo con md5 . La ventaja de usar el cifrado md5 es que el archivo se puede identificar de forma única y también se puede comparar para verificar la integridad del archivo en segundo plano.

2. Después de obtener el valor md5, el servidor verifica si el archivo ya se cargó, si ya se cargó, no es necesario volver a cargarlo.

3. Fragmentar archivos grandes. Por ejemplo, para un archivo de 100M, si uno de nuestros fragmentos es de 5M, entonces este archivo se puede cargar 20 veces.

4. Solicite la interfaz desde el fondo, los datos en la interfaz son el bloque de archivos que hemos cargado. (Nota: ¿Por qué desea enviar esta solicitud? Es solo para poder continuar cargando en los puntos de interrupción . Por ejemplo, usamos el disco de red de Baidu, ¿verdad? Luego, el servidor debe recordar los bloques de archivos que cargué antes, y cuando encienda en la computadora para volver a cargar, luego debe omitir los bloques de archivos que he cargado antes. Luego cargue los bloques posteriores).

5. Comience a cargar bloques de archivos sin cargar. (Esta es la segunda solicitud, que fusionará todos los fragmentos y cargará la solicitud).

6. Después de que la carga sea exitosa, el servidor fusionará los archivos. Terminado por fin.

No hay mucho que decir, solo comienza a programar

<template>
  <div>
  <!-- on-preview	点击文件列表中已上传的文件时的钩子 -->
  <!-- http-request	覆盖默认的上传行为,可以自定义上传的实现 -->
  <!-- limit	最大允许上传个数 -->
  <!-- before-upload	上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。 -->
  <!-- accept	接受上传的文件类型(thumbnail-mode 模式下此参数无效) -->
  <!-- multiple	是否支持多选文件 -->
  <!-- on-change	文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用 -->
  <!-- on-remove	文件列表移除文件时的钩子 -->
  <!-- file-list	上传的文件列表, 例如: [{name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg'}] -->
  <!-- on-exceed	文件超出个数限制时的钩子 -->
  <!-- auto-upload	是否在选取文件后立即进行上传 -->
  <!-- action	必选参数,上传的地址  例如  action="https://jsonplaceholder.typicode.com/posts/"-->
  <el-upload
    drag
    multiple
    :auto-upload="true"
    :http-request="checkedFile"
    :before-remove="removeFile"
    :limit="10"
    action="/tools/upload_test/"
  >
    <i class="el-icon-upload"></i>
    <div class="el-upload__text">
      将文件拖到此处,或
      <em>点击上传</em>
    </div>
  </el-upload>
  <el-progress type="circle" :percentage="progress" class="progress" v-if="showProgress"></el-progress>
</div>
</template>

Cuando se cargue el archivo, se utilizará el método de solicitud http. Si se define este método, submitse interceptará el método del componente (tenga cuidado de no llamar al submitmétodo del componente en este método, lo que provocará un bucle infinito ) En este método, puedo hacer lo que quiero hacer.

La función de devolución de llamada entrante de http-request debería devolver uno Promise, así que definí una Promesa inútil.

const prom = new Promise((resolve, reject) => {})
prom.abort = () => {}
return prom

Si desea implementar cargas reanudables desde puntos de interrupción, debe determinar cómo cooperar con el backend.

Mi plan aquí es que después de cargar todos los fragmentos, solicitaré una interfaz de consulta, y el backend me devolverá en esta interfaz qué fragmentos no se han cargado correctamente (me dará el número de serie). En este momento, Luego vuelve a subir esos fragmentos que no se han subido con éxito

Pegue el código completo directamente, y todos los comentarios están en él. Si no lo entiende, puede contactarme directamente. Hay métodos de contacto en el blog (dependiendo de element-ui, axios, spark-md5)

<template>
  <div>
  <!-- on-preview	点击文件列表中已上传的文件时的钩子 -->
  <!-- http-request	覆盖默认的上传行为,可以自定义上传的实现 -->
  <!-- limit	最大允许上传个数 -->
  <!-- before-upload	上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。 -->
  <!-- accept	接受上传的文件类型(thumbnail-mode 模式下此参数无效) -->
  <!-- multiple	是否支持多选文件 -->
  <!-- on-change	文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用 -->
  <!-- on-remove	文件列表移除文件时的钩子 -->
  <!-- file-list	上传的文件列表, 例如: [{name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg'}] -->
  <!-- on-exceed	文件超出个数限制时的钩子 -->
  <!-- auto-upload	是否在选取文件后立即进行上传 -->
  <!-- action	必选参数,上传的地址  例如  action="https://jsonplaceholder.typicode.com/posts/"-->
  <el-upload
    drag
    multiple
    :auto-upload="true"
    :http-request="checkedFile"
    :before-remove="removeFile"
    :limit="10"
    action="/tools/upload_test/"
  >
    <i class="el-icon-upload"></i>
    <div class="el-upload__text">
      将文件拖到此处,或
      <em>点击上传</em>
    </div>
  </el-upload>
  <el-progress type="circle" :percentage="progress" class="progress" v-if="showProgress"></el-progress>
</div>
</template>
  <script>
import axios from "axios";
import SparkMD5 from "spark-md5";
export default {
  data() {
    return {
      maxSize: 5 * 1024 * 1024 * 1024, // 上传最大文件限制  最小单位是b
      multiUploadSize: 100 * 1024 * 1024, // 大于这个大小的文件使用分块上传(后端可以支持断点续传)  100mb
      eachSize: 100 * 1024 * 1024, // 每块文件大小   100mb
      requestCancelQueue: [], // 请求方法队列(调用取消上传
      url: "/tools/upload_test/",
       //上传进度
      progress: 0,
      showProgress: false,
      // 每上传一块的进度
      eachProgress: 0,
      // 总共有多少块。断点续传使用
      chunksKeep:0,
      // 切割后的文件数组
      fileChunksKeep:[],
      // 这个文件,断点续传
      fileKeep:null
    };
  },
  mounted() {
    
  },
  methods: {
    async checkedFile(options) {
      console.log(options);
      const {
        maxSize,
        multiUploadSize,
        getSize,
        splitUpload,
        singleUpload
      } = this; // 解构赋值
      const { file, onProgress, onSuccess, onError } = options; // 解构赋值
      if (file.size > maxSize) {
        return this.$message({
          message: `您选择的文件大于${getSize(maxSize)}`,
          type: "error"
        });
      }
      this.fileKeep = file
      const uploadFunc =
        file.size > multiUploadSize ? splitUpload : singleUpload; // 选择上传方式
      try {
        await uploadFunc(file, onProgress);
        this.$message({
          message: "上传成功",
          type: "success"
        });
        this.showProgress = false;
        this.progress = 0;
        onSuccess();
      } catch (e) {
        console.error(e);
        this.$message({
          message: e.message,
          type: "error"
        });
        this.showProgress = false;
        this.progress = 0;
        onError();
      }
      const prom = new Promise((resolve, reject) => {}); // 上传后返回一个promise
      prom.abort = () => {};
      return prom;
    },
    // 格式化文件大小显示文字
    getSize(size) {
      return size > 1024
        ? size / 1024 > 1024
          ? size / (1024 * 1024) > 1024
            ? (size / (1024 * 1024 * 1024)).toFixed(2) + "GB"
            : (size / (1024 * 1024)).toFixed(2) + "MB"
          : (size / 1024).toFixed(2) + "KB"
        : size.toFixed(2) + "B";
    },
    // 单文件直接上传
   async singleUpload(file, onProgress) {
      await this.postFile(
        { file, uid: file.uid, fileName: file.fileName ,chunk:0},
        onProgress
      );
      var spark = new SparkMD5.ArrayBuffer();
      spark.append(file);
      var md5 = spark.end();
      console.log(md5);
          const isValidate = await this.validateFile({
            fileName: file.name,
            uid: file.uid,
            md5:md5,
            chunks:1
          });
    },
    // 大文件分块上传
    splitUpload(file, onProgress) {
      console.log('file11')
      console.log(file)
      return new Promise(async (resolve, reject) => {
        try {
          const { eachSize } = this;
          const chunks = Math.ceil(file.size / eachSize);
          this.chunksKeep = chunks
          const fileChunks = await this.splitFile(file, eachSize, chunks);
          this.fileChunksKeep = fileChunks
          console.log('fileChunks,文件数组切割后')
          console.log(fileChunks)
          //判断每上传一个文件,进度条涨多少,保留两位小数
          
          this.eachProgress = parseInt(Math.floor(100 / chunks * 100) / 100);

          this.showProgress = true;
          let currentChunk = 0;
          for (let i = 0; i < fileChunks.length; i++) {
            // 服务端检测已经上传到第currentChunk块了,那就直接跳到第currentChunk块,实现断点续传
            console.log(currentChunk, i);
            // 此时需要判断进度条

            if (Number(currentChunk) === i) {
              // 每块上传完后则返回需要提交的下一块的index
               await this.postFile(
                {
                  chunked: true,
                  chunk: i,
                  chunks,
                  eachSize,
                  fileName: file.name,
                  fullSize: file.size,
                  uid: file.uid,
                  file: fileChunks[i]
                },
                onProgress
              );
              currentChunk++

              // 上传完一块后,进度条增加
              this.progress += this.eachProgress;
              // 不能超过100
              this.progress = this.progress > 100 ? 100 : this.progress;
            }
          }
          var spark = new SparkMD5.ArrayBuffer();
          spark.append(file);
          var md5 = spark.end();
          console.log(md5);
          const isValidate = await this.validateFile({
            chunks: fileChunks.length,
            // chunk: fileChunks.length,
            fileName: file.name,
            uid: file.uid,
            md5:md5,
            // task_id:file.uid
          });
          // if (!isValidate) {
          //   throw new Error("文件校验异常");
          // }
          resolve();
        } catch (e) {
          reject(e);
        }
      });
    },
    againSplitUpload(file, array) {
      console.log('file,array')
      console.log(file)
      console.log(array)
      return new Promise(async (resolve, reject) => {
        try {
          const { eachSize , fileKeep } = this;
          const chunks = this.chunksKeep
          const fileChunks = this.fileChunksKeep
          this.showProgress = true;
          // let currentChunk = 0;
          for (let i = 0; i < array.length; i++) {
            // 服务端检测已经上传到第currentChunk块了,那就直接跳到第currentChunk块,实现断点续传
            // console.log(currentChunk, i);
            // 此时需要判断进度条
              // 每块上传完后则返回需要提交的下一块的index
               await this.postFile(
                {
                  chunked: true,
                  chunk: array[i],
                  chunks,
                  fileName: file.fileName,
                  fullSize: fileKeep.size,
                  uid: file.uid,
                  file: fileChunks[array[i]]
                },
              );
              // currentChunk++

              // 上传完一块后,进度条增加
              // this.progress += this.eachProgress;
              // 不能超过100
              this.progress = this.progress > 100 ? 100 : this.progress;
          }
          var spark = new SparkMD5.ArrayBuffer();
          spark.append(fileKeep);
          var md5 = spark.end();
          console.log(md5);
          const isValidate = await this.validateFile({
            chunks: fileChunks.length,
            // chunk: fileChunks.length,
            fileName: file.fileName,
            uid: file.uid,
            md5:md5,
            // task_id:file.uid
          });
          // if (!isValidate) {
          //   throw new Error("文件校验异常");
          // }
          resolve();
        } catch (e) {
          reject(e);
        }
      });
    },
    // 文件分块,利用Array.prototype.slice方法
    splitFile(file, eachSize, chunks) {
      return new Promise((resolve, reject) => {
        try {
          setTimeout(() => {
            const fileChunk = [];
            for (let chunk = 0; chunks > 0; chunks--) {
              fileChunk.push(file.slice(chunk, chunk + eachSize));
              chunk += eachSize;
            }
            resolve(fileChunk);
          }, 0);
        } catch (e) {
          console.error(e);
          reject(new Error("文件切块发生错误"));
        }
      });
    },
    removeFile(file) {
      this.requestCancelQueue[file.uid]();
      delete this.requestCancelQueue[file.uid];
      return true;
    },
    // 提交文件方法,将参数转换为FormData, 然后通过axios发起请求
    postFile(param, onProgress) {
      console.log(param);
      const formData = new FormData();
      // for (let p in param) {
        // formData.append(p, param[p]);
      // }
      formData.append('file', param.file)  //  改了
      formData.append('uid',param.uid)
      formData.append('chunk',param.chunk)
      const { requestCancelQueue } = this;
      const config = {
        cancelToken: new axios.CancelToken(function executor(cancel) {
          if (requestCancelQueue[param.uid]) {
            requestCancelQueue[param.uid]();
            delete requestCancelQueue[param.uid];
          }
          requestCancelQueue[param.uid] = cancel;
        }),
        onUploadProgress: e => {
          if (param.chunked) {
            e.percent = Number(
              (
                ((param.chunk * (param.eachSize - 1) + e.loaded) /
                  param.fullSize) *
                100
              ).toFixed(2)
            );
          } else {
            e.percent = Number(((e.loaded / e.total) * 100).toFixed(2));
          }
          onProgress(e);
        }
      };
      // return axios.post('/api/v1/tools/upload_test/', formData, config).then(rs => rs.data)
      return this.$http({
        url: "/tools/upload_test/",
        method: "POST",
        
        data: formData
        // config
      }).then(rs => rs.data);
    },
    // 文件校验方法
    validateFile(file) {
      // return axios.post('/api/v1/tools/upload_test/', file).then(rs => rs.data)
      console.log(2)
      console.log(file)
    return  this.$http({
        url: "/tools/upload_test/upload_success/",
        method: "POST",
        data: file
      }).then(res => {
        if(res && res.status == 1){
            this.againSplitUpload(file,res.data.error_file)
          return true
        }
      });
    }
  }
};
</script>
<style scoped>
.progress{
  /* 在当前页面居中 */
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  /* 宽度 */
}
</style>

Actualice el código. Después de cifrar el código anterior con md5, es diferente del valor MD5 cifrado por el backend. El siguiente cifrado es el mismo 

<template>
  <div :class="showProgress == true ? 'loading' : ''">
    <!-- on-preview	点击文件列表中已上传的文件时的钩子 -->
    <!-- http-request	覆盖默认的上传行为,可以自定义上传的实现 -->
    <!-- limit	最大允许上传个数 -->
    <!-- before-upload	上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。 -->
    <!-- accept	接受上传的文件类型(thumbnail-mode 模式下此参数无效) -->
    <!-- multiple	是否支持多选文件 -->
    <!-- on-change	文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用 -->
    <!-- on-remove	文件列表移除文件时的钩子 -->
    <!-- file-list	上传的文件列表, 例如: [{name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg'}] -->
    <!-- on-exceed	文件超出个数限制时的钩子 -->
    <!-- auto-upload	是否在选取文件后立即进行上传 -->
    <!-- action	必选参数,上传的地址  例如  action="https://jsonplaceholder.typicode.com/posts/"-->
    <el-upload drag multiple :auto-upload="true" :http-request="checkedFile" :before-remove="removeFile" :limit="10"
      action="/tools/upload_chunk/" :disabled="showProgress">
      <i class="el-icon-upload"></i>
      <div class="el-upload__text">
        将文件拖到此处,或
        <em>点击上传</em>
      </div>
    </el-upload>
    <!-- 正在上传的弹窗 -->
    <el-dialog title="正在上传" :visible.sync="showProgress" width="50%">
      <el-progress type="circle" :percentage="progress" class="progress" v-if="showProgress"></el-progress>
    </el-dialog>
    <!-- <el-progress type="circle" :percentage="progress" class="progress" v-if="showProgress"></el-progress> -->
  </div>
</template>
<script>
import axios from "axios";
import SparkMD5 from "spark-md5";
export default {
  data() {
    return {
      maxSize: 5 * 1024 * 1024 * 1024, // 上传最大文件限制  最小单位是b
      multiUploadSize: 100 * 1024 * 1024, // 大于这个大小的文件使用分块上传(后端可以支持断点续传)  100mb
      eachSize: 100 * 1024 * 1024, // 每块文件大小   100mb
      requestCancelQueue: [], // 请求方法队列(调用取消上传
      url: "/tools/upload_chunk/",
      //上传进度
      progress: 0,
      showProgress: false,
      // 每上传一块的进度
      eachProgress: 0,
      // 总共有多少块。断点续传使用
      chunksKeep: 0,
      // 切割后的文件数组
      fileChunksKeep: [],
      // 这个文件,断点续传
      fileKeep: null,
      // 断点续传,文件md5
      fileMd5Keep: ""
    };
  },
  mounted() { },
  methods: {
    async checkedFile(options) {
      // console.log(options);
      const {
        maxSize,
        multiUploadSize,
        getSize,
        splitUpload,
        singleUpload
      } = this; // 解构赋值
      const { file, onProgress, onSuccess, onError } = options; // 解构赋值
      if (file.size > maxSize) {
        return this.$message({
          message: `您选择的文件大于${getSize(maxSize)}`,
          type: "error"
        });
      }
      this.fileKeep = file;
      const uploadFunc =
        file.size > multiUploadSize ? splitUpload : singleUpload; // 选择上传方式
      try {
        await uploadFunc(file, onProgress);
        onSuccess();
      } catch (e) {
        console.error(e);
        this.$message({
          message: e.message,
          type: "error"
        });
        this.showProgress = false;
        this.progress = 0;
        onError();
      }
      const prom = new Promise((resolve, reject) => { }); // 上传后返回一个promise
      prom.abort = () => { };
      return prom;
    },
    // 格式化文件大小显示文字
    getSize(size) {
      return size > 1024
        ? size / 1024 > 1024
          ? size / (1024 * 1024) > 1024
            ? (size / (1024 * 1024 * 1024)).toFixed(2) + "GB"
            : (size / (1024 * 1024)).toFixed(2) + "MB"
          : (size / 1024).toFixed(2) + "KB"
        : size.toFixed(2) + "B";
    },
    // 单文件直接上传
    async singleUpload(file, onProgress) {
      await this.postFile(
        { file, uid: file.uid, fileName: file.fileName, chunk: 0 },
        onProgress
      );
      // var spark = new SparkMD5.ArrayBuffer();
      // spark.append(file);
      // var md5 = spark.end();
      // console.log(md5);

      const reader = new FileReader();

      reader.readAsArrayBuffer(file);
      let hashMd5 = "";
      console.log(hashMd5);
      const that = this;
      function getHash(cb) {
        console.log("进入单个上传的getHash");
        reader.onload = function (e) {
          console.log("进入单个上传的getHash的函数2");
          console.log(hashMd5);
          console.log(this);
          // console.log(e)
          const hash = SparkMD5.ArrayBuffer.hash(e.target.result);
          // const hash = SparkMD5.ArrayBuffer.hash(file);
          console.log(hash);
          that.hashMd5 = hash;
          console.log(that.hashMd5);
          that.fileMd5Keep = hash;
          cb(hash);
        };
      }
      await getHash(function (hash) {
        console.log(hash);
        console.log(that);
        // 请求接口
        that.validateFile({
          name: file.name,
          uid: file.uid,
          md5: hash,
          chunks: 1,
          filter_type: "user_data_file"
        });
      });
    },
    // getMd5(file, chunkCount) {
    //   const spark = new SparkMD5.ArrayBuffer();
    //   let currentChunk = 0;

    //   const reader = new FileReader();

    //   reader.onload = function(e) {
    //     spark.append(e.target.result);
    //     currentChunk++;

    //     if (currentChunk < chunkCount) {
    //       console.log(currentChunk);
    //       loadNext();
    //     } else {
    //       console.log(spark.end());
    //       // 在这里请求接口
    //       return spark.end();
    //     }
    //   };

    //   function loadNext() {
    //     const start = currentChunk * chunkSize;
    //     const end =
    //       start + chunkSize >= file.size ? file.size : start + chunkSize;
    //     reader.readAsArrayBuffer(file.slice(start, end));
    //   }

    //   loadNext();
    // },
    // 大文件分块上传
    splitUpload(file, onProgress) {
      return new Promise(async (resolve, reject) => {
        try {
          const { eachSize } = this;
          const chunks = Math.ceil(file.size / eachSize);
          this.chunksKeep = chunks;
          const fileChunks = await this.splitFile(file, eachSize, chunks);
          this.fileChunksKeep = fileChunks;
          console.log("fileChunks,文件数组切割后");
          console.log(fileChunks);
          //判断每上传一个文件,进度条涨多少,保留两位小数
          this.eachProgress = parseInt(Math.floor((100 / chunks) * 100) / 100);
          this.showProgress = true;
          let currentChunk = 0;

          for (let i = 0; i < fileChunks.length; i++) {
            // 服务端检测已经上传到第currentChunk块了,那就直接跳到第currentChunk块,实现断点续传
            console.log(currentChunk, i);
            // 此时需要判断进度条
            if (Number(currentChunk) === i) {
              // 每块上传完后则返回需要提交的下一块的index
              await this.postFile(
                {
                  chunked: true,
                  chunk: i,
                  chunks,
                  eachSize,
                  fileName: file.name,
                  fullSize: file.size,
                  uid: file.uid,
                  file: fileChunks[i]
                },
                onProgress
              );
              currentChunk++;

              // 上传完一块后,进度条增加
              this.progress += this.eachProgress;
              // 不能超过100
              this.progress = this.progress > 100 ? 100 : this.progress;
            }
          }
          // this.getMd5(file, chunks);
          // var spark = new SparkMD5.ArrayBuffer();
          // spark.append(file);
          // var md5 = spark.end();
          // console.log(md5);
          const spark = new SparkMD5.ArrayBuffer();
          let currentChunkMd5 = 0;
          const that = this;
          const reader = new FileReader();
          reader.onload = async function (e) {
            spark.append(e.target.result);
            currentChunkMd5++;

            if (currentChunkMd5 < chunks) {
              loadNext();
            } else {
              // console.log(spark.end());
              var hashMd5111 = spark.end();
              that.fileMd5Keep = hashMd5111;
              console.log(that);
              console.log(hashMd5111);
              // 在这里请求接口
              await that.validateFile({
                name: file.name,
                uid: file.uid,
                md5: hashMd5111,
                chunks: fileChunks.length,
                filter_type: "git_secret_file"
                // chunk: fileChunks.length,
              });
            }
          };

          async function loadNext() {
            const start = currentChunkMd5 * eachSize;
            const end =
              start + eachSize >= file.size ? file.size : start + eachSize;
            await reader.readAsArrayBuffer(file.slice(start, end));
          }
          this.$message({
            message: "正在进行文件加密校验",
            type: "info"
          });
          await loadNext();
          // let hashMd5 = "";
          // // console.log(hashMd5)
          // const that = this;
          // console.log("进入分片上传的getHash");
          // function getHash(cb) {
          //   reader.onload = function(e) {
          //     console.log("进入分片上传的getHash的函数");
          //     const hash = SparkMD5.ArrayBuffer.hash(e.target.result);
          //     // const hash = SparkMD5.ArrayBuffer.hash(file);
          //     console.log(hash);
          //     that.hashMd5 = hash;
          //     console.log(that.hashMd5);
          //     that.fileMd5Keep = hash;
          //     cb(hash);
          //   };
          //   reader.readAsArrayBuffer(file);
          // }
          // await getHash(function() {
          //   console.log(that);
          //   that.validateFile({
          //     name: file.name,
          //     uid: file.uid,
          //     md5: that.hashMd5,
          //     chunks: fileChunks.length
          //     // chunk: fileChunks.length,
          //   });
          // });
          // 请求接口

          // console.log('fileChunks.length')
          // 请求接口
          // this.validateFile({
          //   fileName: file.name,
          //   uid: file.uid,
          //   md5:md5,
          //   chunks:1
          // });
          resolve();
        } catch (error) {
          reject(error);
        }
      });
    },
    // 断点续传
    againSplitUpload(file, array) {
      console.log("file,array");
      console.log(file);
      console.log(array);
      return new Promise(async (resolve, reject) => {
        try {
          const { eachSize, fileKeep } = this;
          const chunks = this.chunksKeep;
          const fileChunks = this.fileChunksKeep;
          this.showProgress = true;
          // let currentChunk = 0;
          for (let i = 0; i < array.length; i++) {
            // 服务端检测已经上传到第currentChunk块了,那就直接跳到第currentChunk块,实现断点续传
            // console.log(currentChunk, i);
            // 此时需要判断进度条
            // 每块上传完后则返回需要提交的下一块的index
            await this.postFile({
              chunked: true,
              chunk: array[i],
              chunks,
              name: file.name,
              fullSize: fileKeep.size,
              uid: file.uid,
              file: fileChunks[array[i]]
            });
            // currentChunk++

            // 上传完一块后,进度条增加
            // this.progress += this.eachProgress;
            // 不能超过100
            this.progress = this.progress > 100 ? 100 : this.progress;
          }
          // var spark = new SparkMD5.ArrayBuffer();
          // spark.append(fileKeep);
          // var md5 = spark.end();
          // console.log(md5);

          var fileMd5KeepTwo = this.fileMd5Keep;

          const isValidate = await this.validateFile({
            chunks: fileChunks.length,
            // chunk: fileChunks.length,
            name: file.name,
            uid: file.uid,
            md5: fileMd5KeepTwo,
            filter_type: "git_secret_file"
            // task_id:file.uid
          });
          // if (!isValidate) {
          //   throw new Error("文件校验异常");
          // }
          // 关闭进度条
          this.showProgress = false;
          // 重置进度条
          this.progress = 0;
          resolve();
        } catch (e) {
          reject(e);
        }
      });
    },
    // 文件分块,利用Array.prototype.slice方法
    splitFile(file, eachSize, chunks) {
      return new Promise((resolve, reject) => {
        try {
          setTimeout(() => {
            const fileChunk = [];
            for (let chunk = 0; chunks > 0; chunks--) {
              fileChunk.push(file.slice(chunk, chunk + eachSize));
              chunk += eachSize;
            }
            resolve(fileChunk);
          }, 0);
        } catch (e) {
          console.error(e);
          reject(new Error("文件切块发生错误"));
        }
      });
    },
    removeFile(file) {
      this.requestCancelQueue[file.uid]();
      delete this.requestCancelQueue[file.uid];
      return true;
    },
    // 提交文件方法,将参数转换为FormData, 然后通过axios发起请求
    postFile(param, onProgress) {
      // console.log(param);
      const formData = new FormData();
      // for (let p in param) {
      // formData.append(p, param[p]);
      // }
      formData.append("file", param.file); //  改了
      formData.append("uid", param.uid);
      formData.append("chunk", param.chunk);
      formData.append("filter_type", "git_secret_file");
      const { requestCancelQueue } = this;
      const config = {
        cancelToken: new axios.CancelToken(function executor(cancel) {
          if (requestCancelQueue[param.uid]) {
            requestCancelQueue[param.uid]();
            delete requestCancelQueue[param.uid];
          }
          requestCancelQueue[param.uid] = cancel;
        }),
        onUploadProgress: e => {
          if (param.chunked) {
            e.percent = Number(
              (
                ((param.chunk * (param.eachSize - 1) + e.loaded) /
                  param.fullSize) *
                100
              ).toFixed(2)
            );
          } else {
            e.percent = Number(((e.loaded / e.total) * 100).toFixed(2));
          }
          onProgress(e);
        }
      };
      // return axios.post('/api/v1/tools/upload_chunk/', formData, config).then(rs => rs.data)
      return this.$http({
        url: "/tools/upload_chunk/",
        method: "POST",

        data: formData
        // config
      }).then(rs => rs.data);
    },
    // 文件校验方法
    validateFile(file) {
      // return axios.post('/api/v1/tools/upload_chunk/', file).then(rs => rs.data)
      return this.$http({
        url: "/tools/upload_chunk/upload_success/",
        method: "POST",
        data: file
      }).then(res => {
        if (res && res.status == 1) {
          this.againSplitUpload(file, res.data.error_file);
          this.$message({
            message: "有文件上传失败,正在重新上传",
            type: "warning"
          });
        } else if (res && res.status == 0) {
          this.$message({
            message: "上传成功",
            type: "success"
          });
          this.showProgress = false;
          this.progress = 0;
        } else if (res && res.status == 40008) {
          this.$message.error(res.message);
          this.showProgress = false;
          this.progress = 0;
        }
      });
    }
  }
};
</script>
<style scoped>
.loading {
  /* 整体页面置灰 */
  /* background: rgba(0, 0, 0, 0.5); */
}

.progress {
  /* 在当前页面居中 */
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  margin-top: 40px;
  /* 宽度 */
}

/deep/ .el-dialog {
  position: relative;
  height: 500px;
}
</style>

Supongo que te gusta

Origin blog.csdn.net/yjxkq99/article/details/128942133
Recomendado
Clasificación