Office Address

  • 123/A, Miranda City Prikano
  • +0989 7876 9865 9
  • info@example.com

Social List

APEX_ZIP: Crea y Descarga Archivos ZIP desde Oracle APEX sin Esfuerzo

Lectura: 5 minutos

Si alguna vez necesitaste que tu aplicación Oracle APEX descargara múltiples archivos de una sola vez, o comprimir reportes dinámicos para tus usuarios, el paquete APEX_ZIP es exactamente lo que estabas buscando.

En este artículo te muestro, con ejemplos reales y listos para usar, cómo sacarle el máximo provecho a esta poderosa API disponible desde Oracle APEX 5.1 y mejorada en la versión 24.2.

¿Qué es APEX_ZIP?

APEX_ZIP es un paquete PL/SQL nativo de Oracle APEX que permite crear, comprimir y descomprimir archivos en formato ZIP directamente desde la base de datos, sin necesidad de herramientas externas, utilidades del sistema operativo ni Java. Todo ocurre dentro del motor de la base de datos Oracle, lo que lo hace extremadamente eficiente y seguro.

El paquete trabaja con tipos de dato BLOB, lo que significa que los archivos ZIP viven completamente en memoria o en tablas de base de datos, y pueden ser descargados mediante botones de descarga de APEX o enviados como adjuntos de correo con APEX_MAIL.

Subprogramas del paquete APEX_ZIP

La API está compuesta por cuatro subprogramas principales que trabajan en conjunto:

SubprogramaDescripción
ADD_FILEAgrega un archivo (como BLOB) al contenedor ZIP que estás construyendo.
FINISHFinaliza y cierra el archivo ZIP, dejándolo listo en un BLOB para usar.
GET_DIR_ENTRIESRetorna la lista de archivos contenidos dentro de un ZIP existente.
GET_FILE_CONTENTExtrae el contenido de un archivo específico dentro de un ZIP.
Nota sobre versiones Las funciones GET_FILES y GET_FILE_CONTENT Signature 1 están marcadas como deprecadas en APEX 24.2. Usa siempre GET_DIR_ENTRIES y GET_FILE_CONTENT Signature 2 para código nuevo.

Ejemplo 1: Crear un ZIP con múltiples archivos de texto

El caso más básico: generar un archivo ZIP con varios documentos de texto dinámico y ofrecerlo como descarga al usuario.

DECLARE
  l_zip  BLOB;
  l_file BLOB;
BEGIN
  -- Inicializamos el contenedor ZIP vacío
  DBMS_LOB.createtemporary(l_zip, TRUE);
 
  -- Archivo 1: Instrucciones
  l_file := UTL_RAW.cast_to_raw(
    'Bienvenido al sistema de reportes.' || CHR(10) ||
    'Este archivo fue generado automaticamente.'
  );
  APEX_ZIP.ADD_FILE(
    p_zipped_blob => l_zip,
    p_file_name   => 'instrucciones.txt',
    p_content     => l_file
  );
 
  -- Archivo 2: Datos de muestra
  l_file := UTL_RAW.cast_to_raw(
    'ID,Nombre,Monto' || CHR(10) ||
    '1,Juan Perez,1500.00' || CHR(10) ||
    '2,Maria Lopez,2300.50'
  );
  APEX_ZIP.ADD_FILE(
    p_zipped_blob => l_zip,
    p_file_name   => 'datos/reporte_ventas.csv',
    p_content     => l_file
  );
 
  -- Cerramos y finalizamos el ZIP
  APEX_ZIP.FINISH(p_zipped_blob => l_zip);
 
  -- Descarga inmediata al navegador del usuario
  OWA_UTIL.mime_header('application/zip', FALSE);
  HTP.p('Content-Length: ' || DBMS_LOB.getlength(l_zip));
  HTP.p('Content-Disposition: attachment; filename="reporte_ventas.zip"');
  OWA_UTIL.http_header_close;
  WPG_DOCLOAD.download_file(l_zip);
  APEX_APPLICATION.stop_apex_engine;
END;
🔑 Punto clave: rutas dentro del ZIP Nota cómo en p_file_name usamos ‘datos/reporte_ventas.csv’. APEX_ZIP soporta subdirectorios dentro del archivo ZIP, lo que te permite organizar los archivos como si fuera una estructura de carpetas real.

Ejemplo 2: comprimir documentos PDF desde la base de datos

Un caso muy común en aplicaciones empresariales: el usuario selecciona varios registros de una tabla que contienen PDFs almacenados como BLOB, y descarga todos en un solo ZIP.

-- Botón de descarga masiva de facturas en formato PDF
DECLARE
  l_zip     BLOB;
  l_archivo BLOB;
  l_nombre  VARCHAR2(200);
BEGIN
  DBMS_LOB.createtemporary(l_zip, TRUE);
 
  -- Iteramos sobre facturas seleccionadas por el usuario
  FOR r IN (
    SELECT f.numero_factura,
           f.pdf_content,
           f.proveedor_nombre
    FROM   facturas f
    WHERE  f.id_factura IN (
             SELECT column_value
             FROM   apex_string.split(:P10_SELECTED_IDS, ':')
           )
    AND    f.pdf_content IS NOT NULL
  ) LOOP
 
    l_nombre := 'facturas/' || r.proveedor_nombre || '_' || r.numero_factura || '.pdf';
 
    APEX_ZIP.ADD_FILE(
      p_zipped_blob => l_zip,
      p_file_name   => l_nombre,
      p_content     => r.pdf_content
    );
  END LOOP;
 
  APEX_ZIP.FINISH(p_zipped_blob => l_zip);
 
  -- Descargar el ZIP
  OWA_UTIL.mime_header('application/zip', FALSE);
  HTP.p('Content-Disposition: attachment; filename="facturas_seleccionadas.zip"');
  HTP.p('Content-Length: ' || DBMS_LOB.getlength(l_zip));
  OWA_UTIL.http_header_close;
  WPG_DOCLOAD.download_file(l_zip);
  APEX_APPLICATION.stop_apex_engine;
END;

Ejemplo 3: Descomprimir un ZIP subido por el usuario

APEX_ZIP también funciona en sentido inverso: puedes procesar un archivo ZIP que el usuario sube mediante un File Browser Item y extraer cada archivo que contiene.

-- Process Row de un File Browse Item (P5_ZIP_FILE)
DECLARE
  l_zip_blob    BLOB;
  l_dir_entries APEX_ZIP.T_FILE_LIST;
  l_file_blob   BLOB;
BEGIN
  -- Obtenemos el BLOB del archivo subido por el usuario
  SELECT blob_content
  INTO   l_zip_blob
  FROM   apex_application_temp_files
  WHERE  name = :P5_ZIP_FILE;
 
  -- Listamos los archivos dentro del ZIP
  l_dir_entries := APEX_ZIP.GET_DIR_ENTRIES(p_zipped_blob => l_zip_blob);
 
  IF l_dir_entries IS NOT NULL THEN
    FOR i IN 1 .. l_dir_entries.COUNT LOOP
 
      -- Ignoramos las entradas de directorio (terminan en /)
      IF SUBSTR(l_dir_entries(i), -1) != '/' THEN
 
        -- Extraemos el contenido de cada archivo
        l_file_blob := APEX_ZIP.GET_FILE_CONTENT(
          p_zipped_blob => l_zip_blob,
          p_file_name   => l_dir_entries(i)
        );
 
        -- Insertamos en nuestra tabla de documentos
        INSERT INTO documentos_cargados (
          nombre_archivo,
          contenido,
          fecha_carga,
          usuario
        ) VALUES (
          l_dir_entries(i),
          l_file_blob,
          SYSDATE,
          :APP_USER
        );
      END IF;
    END LOOP;
    COMMIT;
  END IF;
END;

Ejemplo 4: Backup automático de tablas en CSV dentro de un ZIP

Un patrón avanzado muy útil para reportes nocturnos o auditorías: generar CSVs de múltiples tablas y empacarlos en un ZIP para almacenamiento o envío por correo.

-- Job nocturno que genera backup de datos en ZIP y lo almacena
CREATE OR REPLACE PROCEDURE genera_backup_zip AS
  l_zip   BLOB;
  l_csv   CLOB;
  l_raw   BLOB;
  l_dest  BLOB;
  l_warn  NUMBER;
BEGIN
  DBMS_LOB.createtemporary(l_zip, TRUE);
 
  -- ── Tabla 1: supplier ──────────────────────
  l_csv := 'ID,NOMBRE,RFC,EMAIL,ACTIVO' || CHR(10);
  FOR r IN (SELECT * FROM supplier) LOOP
    l_csv := l_csv || r.id_supplier || ',' || r.nombre || ','
                   || r.rfc || ',' || r.email || ',' || r.activo || CHR(10);
  END LOOP;
 
  DBMS_LOB.createtemporary(l_raw, TRUE);
  DBMS_LOB.converttoblob(
    dest_lob     => l_raw,
    src_clob     => l_csv,
    amount       => DBMS_LOB.lobmaxsize,
    dest_offset  => 1,
    src_offset   => 1,
    blob_csid    => NLS_CHARSET_ID('AL32UTF8'),
    lang_context => 0,
    warning      => l_warn
  );
 
  APEX_ZIP.ADD_FILE(
    p_zipped_blob => l_zip,
    p_file_name   => 'backup/' || TO_CHAR(SYSDATE,'YYYY-MM-DD') || '/suppliers.csv',
    p_content     => l_raw
  );
  
  -- Finalizamos y guardamos en tabla de backups
  APEX_ZIP.FINISH(p_zipped_blob => l_zip);
 
  INSERT INTO backups_sistema (fecha, tipo, archivo_zip)
  VALUES (SYSDATE, 'DIARIO', l_zip);
  COMMIT;
END;

¿Cuándo usar APEX_ZIP? casos de uso reales

Basado en experiencia real con aplicaciones Oracle APEX en entornos productivos, estos son los escenarios donde APEX_ZIP brilla con luz propia:

✅ Exportación masiva de reportes

Cuando un usuario solicita exportar cientos de registros con sus archivos adjuntos, APEX_ZIP los empaca en segundos. Ideal para cierres mensuales, auditorías y generación de expedientes.

✅ Carga masiva de documentos

Los departamentos de captura pueden subir un solo ZIP con decenas de documentos (XML, PDF, imágenes) que la aplicación procesa y distribuye automáticamente en las tablas correctas.

✅ Backups programados desde DBMS_SCHEDULER

Genera snapshots de datos críticos en formato CSV comprimido y almacénalos en la base de datos o envíalos por correo electrónico como adjunto usando APEX_MAIL.

Tips profesionales para usar APEX_ZIP

⚡ Tip 1: Siempre libera la memoria temporal Después de usar el ZIP, si no lo estás almacenando en base de datos, llama a DBMS_LOB.freetemporary(l_zip) para liberar la memoria de la SGA. En páginas de alta concurrencia esto es crítico.
🗂️ Tip 2: Usa nombres de archivo con rutas para organización El parámetro p_file_name acepta barras ‘/’ para crear subcarpetas virtuales dentro del ZIP. Usa esta característica para estructurar los archivos: ‘enero/ventas.csv’, ‘enero/compras.csv’.
🔒 Tip 3: Combina con APEX_UTIL.get_blob_file_src para seguridad Si guardas el ZIP en una tabla, puedes controllar quién puede descargarlo usando la autorización de APEX, en lugar de exponer el BLOB directamente mediante una URL.
📧 Tip 4: Adjunta ZIP en correos con APEX_MAIL Usa APEX_MAIL.ADD_ATTACHMENT pasando el BLOB del ZIP con mime_type ‘application/zip’. Perfecto para envío de reportes mensuales de forma automática por DBMS_SCHEDULER.

Resumen

APEX_ZIP es una de las APIs más versátiles y subestimadas de Oracle APEX. Con solo cuatro subprogramas (ADD_FILE, FINISH, GET_DIR_ENTRIES, GET_FILE_CONTENT) puedes resolver problemas complejos de manejo de archivos de forma elegante, sin depender de nada externo a la base de datos.

Es especialmente poderoso cuando lo combinas con otras APIs del ecosistema APEX como APEX_MAIL, APEX_DATA_EXPORT y APEX_STRING, creando flujos de trabajo completamente automatizados que agregan un valor real a tus aplicaciones empresariales.

¿Ya usabas APEX_ZIP en tus proyectos? Déjame saber en los comentarios qué caso de uso te resultó más útil.

Referencias

📚 Documentación oficial: https://docs.oracle.com/en/database/oracle/apex/24.2/aeapi/APEX_ZIP.html

📚 Oracle APEX 24.2 API Reference — Oracle Corporation

Posts sugeridos

Post a Comment

Your email address will not be published. Required fields are marked *