Implementación de carga fragmentada para archivos de gran tamaño y directorios con PHP y Vue 3

El manejo de archivos que superan los 20 GB en entornos web presenta desafíos críticos, especialmente en cuanto a la estabilidad de la conexión y la integridad de los datos. Una solución robusta requiere la implementación de técnicas de fragmentación (chunking), soporte para estructuras de carpetas recursivas y capas de abstracción para múltiples motores de base de datos.

1. Arquitectura del Frontend: Vue 3 y WebUploader

Para garantizar la compatibilidad con diversos navegadores y permitir la carga de archivos masivos, se utiliza una combinación de Vue 3 y WebUploader. La funcionalidad clave es la capacidad de procesar directorios manteniendo su jerarquía original mediante la API webkitGetAsEntry.


// Lógica para el procesamiento de carpetas y cifrado opcional
import WebUploader from 'webuploader';
import CryptoJS from 'crypto-js';

const listaDeArchivos = ref([]);

const procesarCarpeta = (evento) => {
  const items = evento.target.files;
  
  const recorrerEntradas = (item, rutaBase) => {
    if (item.isFile) {
      item.file(archivo => {
        // Ejemplo de cifrado de metadatos o contenido
        const lector = new FileReader();
        lector.onload = (e) => {
          const contenidoCifrado = CryptoJS.AES.encrypt(
            e.target.result, 
            'clave-segura-123'
          ).toString();
          
          listaDeArchivos.value.push({
            datos: archivo,
            rutaRelativa: rutaBase + archivo.name,
            hashCifrado: contenidoCifrado
          });
        };
        lector.readAsDataURL(archivo);
      });
    } else if (item.isDirectory) {
      const lectorDirectorio = item.createReader();
      lectorDirectorio.readEntries(entradas => {
        entradas.forEach(entrada => {
          recorrerEntradas(entrada, rutaBase + item.name + '/');
        });
      });
    }
  };

  Array.from(items).forEach(item => recorrerEntradas(item, ''));
};

const configurarUploader = () => {
  const uploader = WebUploader.create({
    server: '/api/recepcion_fragmentos.php',
    chunked: true,
    chunkSize: 5 * 1024 * 1024, // Bloques de 5MB
    threads: 3,
    formData: { token: 'auth_token_example' }
  });

  uploader.on('uploadSuccess', (archivo, respuesta) => {
    console.info('Fragmento procesado correctamente:', respuesta);
  });
};

2. Procesamiento en el Servidor (PHP)

El backend debe encargarse de recibir cada fragmento, almacenarlo temporalmente y unificarlo una vez que todas las partes han sido transmitidas con éxito. A continuación, se presenta una lógica optimizada para la consolidación de archivos.


<?php
// recepcion_fragmentos.php

$directorioTemporal = "./storage/tmp/" . $_POST['file_id'];
$nombreArchivoFinal = "./storage/final/" . $_POST['file_name'];

if (!file_exists($directorioTemporal)) {
    mkdir($directorioTemporal, 0777, true);
}

// Almacenar el fragmento actual
move_uploaded_file($_FILES['file']['tmp_name'], $directorioTemporal . '/' . $_POST['chunk_index']);

$totalFragmentos = (int)$_POST['chunks'];
$fragmentosRecibidos = count(glob("$directorioTemporal/*"));

if ($fragmentosRecibidos === $totalFragmentos) {
    $archivoFinal = fopen($nombreArchivoFinal, 'wb');
    
    for ($i = 0; $i < $totalFragmentos; $i++) {
        $rutaFragmento = "$directorioTemporal/$i";
        $contenido = file_get_contents($rutaFragmento);
        fwrite($archivoFinal, $contenido);
        unlink($rutaFragmento); // Eliminar fragmento tras unirlo
    }
    
    fclose($archivoFinal);
    rmdir($directorioTemporal);
    
    echo json_encode(['status' => 'success', 'message' => 'Archivo consolidado']);
} else {
    echo json_encode(['status' => 'uploading', 'progress' => ($fragmentosRecibidos / $totalFragmentos) * 100]);
}
?>

3. Abstracción de Base de Datos para Entornos Corporativos

En sistemas que requieren alta compatibilidad (como entornos con bases de datos MySQL, PostgreSQL o sistemas propietarios como Dameng), es imperativo utilizar una fábrica de conexiones basada en PDO.


<?php
class GestorBaseDatos {
    public static function conectar($driver, $config) {
        try {
            switch ($driver) {
                case 'mysql':
                    $dsn = "mysql:host={$config['host']};dbname={$config['db']}";
                    break;
                case 'postgresql':
                    $dsn = "pgsql:host={$config['host']};dbname={$config['db']}";
                    break;
                case 'oracle':
                    $dsn = "oci:dbname={$config['host']}/{$config['db']}";
                    break;
                default:
                    throw new Exception("Driver no soportado");
            }
            return new PDO($dsn, $config['user'], $config['pass'], [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
            ]);
        } catch (PDOException $e) {
            die("Error de conexión: " . $e->getMessage());
        }
    }
}
?>

4. Optimización de la Persistencia

Para asegurar que las cargas puedan reanudarse tras un cierre inesperado del navegador o una caída de red, se recomienda:

  • Utilizar localStorage en el cliente para registrar el progreso de cada file_id.
  • Implementar una verificación previa (pre-flight) en el servidor que informe al cliente qué fragmentos ya existen para evitar retransmisiones innecesarias.
  • Configurar el servidor PHP para manejar tiempos de ejecución prolongados (max_execution_time) y límites de memoria adecuados, aunque el chunking minimiza este impacto.

El uso de herramientas de almacenamiento en la nube, como AWS S3 o Huawei Cloud OBS, puede integrarse reemplazando la lógica de escritura local por los respectivos SDKs, manteniendo la misma estructura de fragmentación en el lado del cliente.

Etiquetas: PHP vue.js WebUploader carga de archivos Fragmentación de archivos

Publicado el 7-24 08:24