Subida de archivos grandes (subida por fragmentos y continuación)

1.Componente el-upload para subida de archivos

<template>
  <div>
    <!-- componente el-upload para subir archivos -->
    <el-upload
      ref="upload"
      :action="uploadUrl"
      :before-upload="prepararSubida"
      :on-change="procesarCambio"
      :on-success="procesarExito"
      :on-error="procesarError"
      :file-list="listaArchivos"
      :auto-upload="false"
    >
      <!-- botón para seleccionar archivo -->
      <el-button slot="trigger" size="small" type="primary">Seleccionar archivo</el-button>
      <!-- botón para iniciar subida -->
      <el-button slot="upload" size="small" type="success" @click="iniciarSubida">Iniciar subida</el-button>
    </el-upload>

    <!-- barra de progreso de subida -->
    <el-progress :percentage="progreso"></el-progress>
  </div>
</template>

data() {
    return {
      uploadUrl: 'https://tu-servidor/subir',  // endpoint del servidor
      listaArchivos: [],
      progreso: 0,  // progreso de subida
      tamanhoFragmento: 5 * 1024 * 1024,  // tamaño de cada fragmento 5MB
    };
  },

Cálculo de fragmentos del archivo antes de la subida``` async prepararSubida(archivo) { const totalFragmentos = Math.ceil(archivo.size / this.tamanhoFragmento)//calcular número total de fragmentos const listaFragmentos = [] //almacenar todos los fragmentos // dividir el archivo en múltiples bloques for(let i = 0; i < totalFragmentos; i++){ const inicio = i * this.tamanhoFragmento // posición de inicio del fragmento actual const fin = Math.min(inicio + this.tamanhoFragmento, archivo.size) // posición de fin del fragmento actual listaFragmentos.push(archivo.slice(inicio, fin)) //extraer la parte del archivo desde inicio hasta fin y devolver un nuevo objeto Blob } //agregar fragmentos a listaArchivos para perparar subida this.listaFragmentos = listaFragmentos.map((frgamento, indice) => { return { raw: fragmento, // fragmento actual nombre: archivo.name, // nombre del archivo indiceFragmento: indice, // índice del fragmento actual totalFragmentos: totalFragmentos,// total de fragmentos hashArchivo: await this.calcularHashArchivo(archivo),//identificador único del archivo
} }) return false }


Cifrado```
// calcular el valor hash del archivo (identificador único) npm install spark-md5 --save
    calcularHashArchivo(archivo) {
      const spark = new SparkMD5.ArrayBuffer();  // usar SparkMD5 para calcular el hash
      const lector = new FileReader();
      return new Promise((resolve, reject) => {
        lector.onload = function(e) {
          spark.append(e.target.result);
          resolve(spark.end()); // devolver el hash calculado
        };
        lector.onerror = function(error) {
          reject(error);
        };
        lector.readAsArrayBuffer(archivo);  // leer contenido del archivo
      });
    },

Al hacer clic en iniciar subida``` iniciarSubida(){ this.listaFragmentos.forEach((objArchivo) => { this.subirFragmento(objArchivo) //subir cada fragmento }) }



subirFragmento(objArchivo){
    // verificar si el fragmento actual ya fue subido
    this.obtenerFragmentosSubidos(objArchivo.nombre).then(fragmentosSubidos => {
    if (fragmentosSubidos.includes(objArchivo.indiceFragmento)) {
       console.log(`Fragmento ${objArchivo.indiceFragmento} ya fue subido, omitir`);
       return;  // si el fragmento ya fue subido, omitir la subida
    }
    // subir fragmentos no-subidos
    const formData = new FormData();  // crear instancia FormData para preparar datos
    formData.append('file', objArchivo.raw);  // fragmento actual
    formData.append('chunkIndex', objArchivo.indiceFragmento);  // índice del fragmento actual
    formData.append('totalChunks', objArchivo.totalFragmentos);  // total de fragmentos
    formData.append('fileName', objArchivo.nombre);  // nombre del archivo
    formData.append('fileHash', objArchivo.hashArchivo);  // identificador único del archivo (hash)

    // usar fetch para realizar solicitud de subida
    this.subirArchivo(formData, objArchivo)
      .then(respuesta => {
        this.procesarExito(objArchivo, respuesta);  // al subir exitosamente procesar
      })
      .catch(error => {
        this.procesarError(objArchivo, error);  // al fallar la subida procesar
      });
  });
}


Obtener índices de fragmentos subidos```

    obtenerFragmentosSubidos(nombreArchivo) {
      return fetch(`https://tu-servidor/obtenerFragmentosSubidos?fileName=${nombreArchivo}`)
        .then(respuesta => respuesta.json())
        .then(datos => datos.fragmentosSubidos); // devolver array de índices de fragmentos subidos
    },

Llamada a la interfaz de subida``` subirArchivo(formData, objArchivo) { return new Promise((resolve, reject) => { fetch(this.uploadUrl, { method: 'POST', headers: this.encabezados, body: formData, }) .then(respuesta => respuesta.json()) // parsear respuesta JSON .then(datos => { if (datos.estado === 'exito') { this.actualizarFragmentosSubidos(objArchivo.nombre, objArchivo.indiceFragmento); // actualizar fragmentos subidos resolve(datos); // al subir exitosamente, devolver datos de éxito } else { reject(new Error('Error en la subida')); // al fallar la subida, devolver error } }) .catch(err => { reject(err); // al fallar la solicitud, devolver error }); }); },



// actualizar índice de fragmentos subidos
actualizarFragmentosSubidos(nombreArchivo, indiceFragmento) {
  let fragmentosSubidos = JSON.parse(localStorage.getItem(nombreArchivo)) || [];
  if (!fragmentosSubidos.includes(indiceFragmento)) {
    fragmentosSubidos.push(indiceFragmento);
  }
  localStorage.setItem(nombreArchivo, JSON.stringify(fragmentosSubidos)); // guardar en localStorage
},


Etiquetas: JavaScript vue.js element-ui archivos-grandes subida-http

Publicado el 7-21 04:11