Exportación de datos de consulta a Excel con generación de cadena Base64 para descarga

Para implementar una funcionalidad que permita exportar datos de consulta directamente a un archivo Excel, primero necesitamos definir una estructura de encabezados. Esta clase mapea los campos de datos a sus respectivos nombres de columna en la hoja de cálculo.


package model.export;

import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Configuración de encabezados para exportación Excel
 */
public class ExcelColumnConfig implements Serializable {
    
    private static final long serialVersionUID = 8943726192837465L;
    
    private String dataField;
    private String displayLabel;
    
    public ExcelColumnConfig(String dataField, String displayLabel) {
        this.dataField = dataField;
        this.displayLabel = displayLabel;
    }
    
    public Map<String, String> buildColumnMapping() {
        Map<String, String> mapping = new LinkedHashMap<>();
        mapping.put("code", "Identificador");
        mapping.put("description", "Descripción");
        mapping.put("transactionRef", "Referencia");
        mapping.put("status", "Estado");
        mapping.put("errorCode", "Código Error");
        mapping.put("errorMessage", "Mensaje Error");
        mapping.put("timestamp", "Fecha/Hora");
        mapping.put("operator", "Operador");
        mapping.put("originIp", "Dirección IP");
        mapping.put("duration", "Duración (ms)");
        mapping.put("createdAt", "Creación");
        mapping.put("modifiedAt", "Modificación");
        return mapping;
    }
    
    // Getters y setters omitidos por brevedad
}

El servicio principal se encarga de obtener los datos de consulta, generar el archivo Excel temporalmente y convertirlo a formato Base64 para transmisión al cliente.


package service.export;

import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import model.export.ExcelColumnConfig;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Value;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
import java.util.List;
import java.util.Map;

@Service
public class DataExportService {
    
    @Value("${export.max.rows:50000}")
    private int maximumExportRows;
    
    /**
     * Genera representación Base64 del archivo Excel
     * @param dataList Lista de datos a exportar
     * @return Cadena Base64 del archivo Excel
     */
    public String generateExcelBase64(List<Map<String, Object>> dataList) {
        validateDataSize(dataList);
        
        ExcelColumnConfig config = new ExcelColumnConfig();
        Map<String, String> columnMap = config.buildColumnMapping();
        
        Path tempFile = null;
        try {
            tempFile = Files.createTempFile("export_", ".xlsx");
            ExcelWriter writer = ExcelUtil.getWriter(tempFile.toFile());
            
            configureColumns(writer, columnMap);
            writer.write(dataList, true);
            writer.close();
            
            return encodeFileToBase64(tempFile);
        } catch (IOException e) {
            throw new RuntimeException("Error en exportación: " + e.getMessage(), e);
        } finally {
            cleanupTempFile(tempFile);
        }
    }
    
    private void validateDataSize(List<?> data) {
        if (data.size() > maximumExportRows) {
            throw new IllegalStateException(
                String.format("Límite excedido: %d filas (máximo %d)", 
                data.size(), maximumExportRows));
        }
    }
    
    private void configureColumns(ExcelWriter writer, Map<String, String> columns) {
        columns.forEach((field, header) -> writer.addHeaderAlias(field, header));
    }
    
    private String encodeFileToBase64(Path filePath) throws IOException {
        byte[] fileBytes = Files.readAllBytes(filePath);
        return Base64.getEncoder().encodeToString(fileBytes);
    }
    
    private void cleanupTempFile(Path filePath) {
        if (filePath != null) {
            try {
                Files.deleteIfExists(filePath);
            } catch (IOException ignored) {
                // Log en producción
            }
        }
    }
}

En el front-end, el proceso implica realizar una solicitud al endpoint, recibir la cadena Base64 y convertirla en un archivo descargable usando técnicas modernas de JavaScript.


// Función principal de exportación
async function exportDataToExcel(parameters) {
    const endpoint = '/api/export/generate';
    
    try {
        const response = await fetch(`${endpoint}?${new URLSearchParams(parameters)}`);
        const result = await response.json();
        
        if (!response.ok || !result.success) {
            showError(result.message || 'Error en la exportación');
            return;
        }
        
        downloadExcelFile(result.data, 'consulta_exportada.xlsx');
        
    } catch (error) {
        console.error('Fallo en exportación:', error);
        showError('Error de conexión');
    }
}

function downloadExcelFile(base64Content, fileName) {
    const binaryString = atob(base64Content);
    const byteArray = new Uint8Array(binaryString.length);
    
    for (let i = 0; i < binaryString.length; i++) {
        byteArray[i] = binaryString.charCodeAt(i);
    }
    
    const blob = new Blob([byteArray], {
        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    });
    
    const downloadUrl = URL.createObjectURL(blob);
    const anchor = document.createElement('a');
    
    anchor.href = downloadUrl;
    anchor.download = fileName;
    document.body.appendChild(anchor);
    anchor.click();
    
    // Limpieza de recursos
    setTimeout(() => {
        document.body.removeChild(anchor);
        URL.revokeObjectURL(downloadUrl);
    }, 100);
}

function showError(message) {
    // Implementación del sistema de notificaciones
    alert(message);
}

Para verificar la funcionalidad, se puede implementar un caso de prueba que evalúe el flujo completo de exportación con datos simulados.


// Ejemplo de prueba unitaria
@Test
void testExcelExportProcess() {
    DataExportService exportService = new DataExportService();
    
    // Datos de prueba
    List<Map<String, Object>> testData = Arrays.asList(
        Map.of("code", "001", "description", "Prueba 1", "status", "ACTIVE"),
        Map.of("code", "002", "description", "Prueba 2", "status", "INACTIVE")
    );
    
    String base64Result = exportService.generateExcelBase64(testData);
    
    assertNotNull(base64Result);
    assertTrue(base64Result.length() > 100);
    
    // Verificar que el Base64 es decodificable
    byte[] decodedBytes = Base64.getDecoder().decode(base64Result);
    assertTrue(decodedBytes.length > 0);
}

Etiquetas: java Excel base64 JavaScript Hutool

Publicado el 6-26 23:07