Implementación de Carga de Archivos Múltiples en Spring Boot

La transferencia de archivos desde el cliente al servidor es una funcionalidad esencial en el desarrollo web moderno. En el ecosistema de Spring Boot, esto se gestiona de manera eficiente mediante el manejo de solicitudes multipart. A continuación, se detalla el proceso para implementar un sistema de carga dinámica de archivos utilizando Spring Boot, Thymeleaf y una lógica sencilla de JavaScript.

1. Definición de la Interfaz de Usuario

Para permitir que el usuario seleccione archivos de forma dinámica, crearemos una plantilla Thymeleaf. Esta interfaz incluirá un formulario que permite añadir o eliminar campos de entrada (inputs) de archivos mediante JavaScript.


<html xmlns:th="http://www.thymeleaf.org" lang="es">
<head>
    <meta charset="UTF-8">
    <title>Gestión de Carga de Archivos</title>
    <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
    <script th:src="@{/js/jquery.min.js}"></script>
</head>
<body class="container mt-5">
    <div th:if="${mensaje}" class="alert alert-info" th:text="${mensaje}"></div>

    <form th:action="@{/procesar-carga}" method="post" enctype="multipart/form-data">
        <div class="mb-3">
            <button type="button" class="btn btn-secondary" onclick="agregarCampo()">Añadir nuevo archivo</button>
        </div>
        
        <div id="contenedor-archivos" class="mb-3"></div>

        <input id="boton-enviar" type="submit" value="Iniciar Carga" class="btn btn-primary" style="display: none;"/>
    </form>

    <script type="text/javascript">
        function agregarCampo() {
            let idUnico = Date.now();
            let htmlFila = `<div id="fila-${idUnico}" class="input-group mb-2">
                <input type="file" name="archivos" class="form-control" required>
                <button type="button" class="btn btn-danger" onclick="eliminarCampo('fila-${idUnico}')">Quitar</button>
            </div>`;
            
            $("#contenedor-archivos").append(htmlFila);
            actualizarVisibilidadBoton();
        }

        function eliminarCampo(id) {
            $(`#${id}`).remove();
            actualizarVisibilidadBoton();
        }

        function actualizarVisibilidadBoton() {
            if ($("#contenedor-archivos div").length > 0) {
                $("#boton-enviar").show();
            } else {
                $("#boton-enviar").hide();
            }
        }
    </script>
</body>
</html>

2. Configuración del Servidor

Es fundamental configurar los límites de tamaño para las cargas en el archivo application.properties. Por defecto, Spring Boot tiene restricciones estrictas que pueden bloquear archivos de gran tamaño.

# Deshabilitar caché de Thymeleaf para desarrollo
spring.thymeleaf.cache=false

# Límite máximo para un solo archivo
spring.servlet.multipart.max-file-size=15MB

# Límite máximo para la solicitud completa (múltiples archivos)
spring.servlet.multipart.max-request-size=60MB

3. Implementación del Controlador en Java

El controlador se encargará de recibir el arreglo de archivos, generar nombres únicos para evitar colisinoes y almacenarlos en una ruta específica del sistema de archivos local.

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

@Controller
public class GestionArchivosController {

    @GetMapping("/subir")
    public String mostrarFormulario() {
        return "upload";
    }

    @PostMapping("/procesar-carga")
    public String procesarCarga(MultipartFile[] archivos, Model modelo) {
        String rutaDestino = "C:/uploads/almacen/"; // O /home/usuario/uploads/ en Linux
        File directorio = new File(rutaDestino);
        
        if (!directorio.exists()) {
            directorio.mkdirs();
        }

        for (MultipartFile archivo : archivos) {
            if (!archivo.isEmpty()) {
                try {
                    String nombreOriginal = archivo.getOriginalFilename();
                    // Generar un nombre único preservando la extensión si es necesario
                    String nuevoNombre = UUID.randomUUID().toString() + "_" + nombreOriginal;
                    
                    archivo.transferTo(new File(rutaDestino + nuevoNombre));
                } catch (IOException e) {
                    modelo.addAttribute("mensaje", "Error al procesar: " + e.getMessage());
                    return "upload";
                }
            }
        }

        modelo.addAttribute("mensaje", "¡Archivos subidos correctamente!");
        return "upload";
    }
}

4. Funcionamianto y Validación

Al acceder a la ruta /subir, el usuario encontrará una interfaz limpia. Al pulsar "Añadir nuevo archivo", se generarán dinámicamente elementos de entrada de tipo file. La lógica de JavaScript asegura que el botón de envío solo sea visible cuando haya al menos un archivo pendiente de carga.

Una vez que el usuario presiona "Iniciar Carga", el servidor itera sobre el arreglo archivos. Cada elemento se guarda en el disco duro con un identificador único (UUID). Este enfoque previene la sobreescritura accidental si dos usuarios suben un archivo con el mismo nombre (por ejemplo, documento.pdf) simultáneamente.

Etiquetas: Spring Boot Thymeleaf java MultipartFile JavaScript

Publicado el 6-25 18:47