Cargar archivos con Spring Boot

    Introducci贸n

    Cargar archivos en un sitio web no es una tarea poco com煤n, pero tampoco es muy sencillo de lograr. Algunos casos de uso de por qu茅 querr铆a cargar un archivo en un sitio web incluyen servicios que ofrecen conversiones de archivos en l铆nea y sitios web para compartir fotos. En ciertas aplicaciones, es posible que incluso queramos enviar un archivo a otro usuario, etc.

    La Spring proporciona una MultipartFile interfaz para manejar solicitudes HTTP de varias partes para cargar archivos. Las solicitudes de archivos de varias partes dividen los archivos grandes en trozos m谩s peque帽os, lo que lo hace eficiente para la carga de archivos. Se puede encontrar m谩s informaci贸n al respecto. aqu铆.

    Configuraci贸n del proyecto

    Para demostrar la carga de archivos, crearemos una aplicaci贸n Spring MVC t铆pica que consta de un Controller, un Service para el procesamiento de backend, y Thymeleaf para ver la representaci贸n.

    La forma m谩s sencilla de comenzar con un proyecto esqueleto de Spring Boot, como siempre, es usando Spring Initializr. Seleccione su versi贸n preferida de Spring Boot y agregue el Web y Thymeleaf dependencias:

    Despu茅s de esto, generelo como un proyecto de Maven y 隆listo!

    Construyendo la Aplicaci贸n

    Clase de servicio

    Empecemos por construir el Service capa primero. Lo nombraremos como FileService.java:

    @Service
    public class FileService {
    
        @Value("${app.upload.dir:${user.home}}")
        public String uploadDir;
    
        public void uploadFile(MultipartFile file) {
    
            try {
                Path copyLocation = Paths
                    .get(uploadDir + File.separator + StringUtils.cleanPath(file.getOriginalFilename()));
                Files.copy(file.getInputStream(), copyLocation, StandardCopyOption.REPLACE_EXISTING);
            } catch (Exception e) {
                e.printStackTrace();
                throw new FileStorageException("Could not store file " + file.getOriginalFilename()
                    + ". Please try again!");
            }
        }
    }
    

    Vamos a dividirlo l铆nea por l铆nea:

    • @Service es una especializaci贸n del @Component anotaci贸n. Le dice a Spring que esta es una clase de servicio. Normalmente, toda la l贸gica empresarial se escribe en esta capa.
    • Entonces tenemos una variable uploadDir, que usaremos para almacenar la ruta del directorio en el que queremos que se cargue nuestro archivo. Est谩 anotado con @Value, lo que significa que su valor puede ser establecido por el application.properties archivo por el app.upload.dir llave. En caso de que esta clave no est茅 definida, el valor predeterminado es user.home – que se encuentra en una variable de entorno de cada sistema operativo.
    • Entonces tenemos un m茅todo p煤blico uploadFile que toma en un MultipartFile como argumento.
    • Luego creamos el Path del archivo utilizando el Paths clase proporcionada por Java. StringUtils.cleanPath se usa para limpiar el camino y simplemente agregamos uploadDir a ella usando un File.separator. Utilice siempre m茅todos de utilidad para manejar rutas en el c贸digo porque manejar谩 autom谩ticamente diferentes implementaciones de SO. Por ejemplo, en Windows, el separador de archivos es mientras que en Linux es /.
    • Luego copiamos el archivo a la ubicaci贸n usando Files.copy. los REPLACE_EXISTING La opci贸n de copia anular谩 cualquier archivo con el mismo nombre all铆.
    • Si hay un Exception en todo este proceso lo capturamos y lanzamos una costumbre FileStorageException excepci贸n.

    Excepci贸n personalizada

    Escribimos una costumbre FileStorageException para cualquier excepci贸n durante el proceso de carga del archivo. Es una clase simple que se extiende RuntimeException:

    public class FileStorageException extends RuntimeException {
    
        private static final long serialVersionUID = 1L;
        private String msg;
    
        public FileStorageException(String msg) {
            this.msg = msg;
        }
    
        public String getMsg() {
            return msg;
        }
    }
    

    Para poder usar la excepci贸n de la forma en que lo hicimos, Spring necesita saber c贸mo lidiar con ella si se encuentra. Para eso, hemos creado un AppExceptionHandler que est谩 anotado con @ControllerAdvice y tiene un @ExceptionHandler definido para FileStorageException:

    @ControllerAdvice
    public class AppExceptionHandler {
    
        @ExceptionHandler(FileStorageException.class)
        public ModelAndView handleException(FileStorageException exception, RedirectAttributes redirectAttributes) {
    
            ModelAndView mav = new ModelAndView();
            mav.addObject("message", exception.getMsg());
            mav.setViewName("error");
            return mav;
        }
    }
    

    En el handleException m茅todo simplemente devolvimos el ModelAndView objeto que devolver谩 el error message en una vista de error, que es solo una plantilla de Thymeleaf llamada error.html:

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
      <meta charset="UTF-8">
      <title>ERROR</title>
    </head>
    <body>
      <h1>Error!!!</h1>
      <div th:if="${message}">
        <h2 th:text="${message}"/>
      </div>
    </body>
    </html>
    

    Si desea leer m谩s sobre las excepciones en Java y Spring, lo cubrimos en detalle en los siguientes art铆culos:

    • Manejo de excepciones en Java: una gu铆a completa con las mejores y las peores pr谩cticas
    • C贸mo hacer excepciones personalizadas en Java
    • Manejo de excepciones en Spring

    Controlador y Frontend

    Creemos ahora un sencillo FileController clase que usar谩 el FileService para manejar la carga de archivos:

    @Controller
    public class FileController {
    
        @Autowired
        FileService fileService;
    
        @GetMapping("/")
        public String index() {
            return "upload";
        }
    
        @PostMapping("/uploadFile")
        public String uploadFile(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes) {
    
            fileService.uploadFile(file);
    
            redirectAttributes.addFlashAttribute("message",
                "You successfully uploaded " + file.getOriginalFilename() + "!");
    
            return "redirect:/";
        }
    }
    

    Vamos a dividirlo l铆nea por l铆nea:

    • @Controller La anotaci贸n es tambi茅n una especializaci贸n de @Component anotaci贸n. Hace que una clase acepte la solicitud HTTP y responda en consecuencia. Tambi茅n se encarga de las diversas conversiones de la carga 煤til de la solicitud a una estructura de datos interna.
    • Luego, nosotros @Autowired la FileService frijol para que podamos usar su uploadFile m茅todo.
    • Entonces tenemos un simple GetMapping a / que simplemente devolver谩 String upload. Al ser una clase de controlador, Spring buscar谩 upload.html y s铆rvasela en el navegador.
    • A continuaci贸n, tenemos un PostMapping de /uploadFile, que tienen un RequestParam de MultipartFile que es un objeto que tiene nuestro archivo y sus detalles de metadatos.
    • Luego usamos el FileService uploadFile m茅todo para cargar el archivo. RedirectAttributes es una especializaci贸n de Spring Model interfaz que se utiliza para seleccionar atributos para un escenario de redireccionamiento.
    • Si la operaci贸n anterior es exitosa, configuramos el mensaje de 茅xito en redirectAttributes y redirigir a la misma p谩gina.

    Ahora hagamos otra plantilla de Thymeleaf,upload.html:

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    <body>
      <h1>Spring Boot File Upload Example</h1>
      <hr/>
      <h4>Upload Single File:</h4>
      <form method="POST" th:action="@{/uploadFile}" enctype="multipart/form-data">
        <input type="file" name="file"/> <br/><br/>
        <button type="submit">Submit</button>
      </form>
      <hr/>
      <div th:if="${message}">
        <h2 th:text="${message}"/>
      </div>
    </body>
    </html>
    

    Arriba, tenemos un sencillo form que se asigna al /uploadFile URL. Note que es enctype es multipart/form-data y input escribir como file. En la parte inferior tiene un mensaje div para mostrar el mensaje de 茅xito.

    Nuestra clase principal es una clase principal t铆pica de Spring Boot:

    @SpringBootApplication
    public class FileIoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(FileIoApplication.class, args);
        }
    }
    

    Ejecutemos nuestra aplicaci贸n y naveguemos a http://localhost:8080:

    Elija un archivo y c谩rguelo, deber铆a ver algo como:

    Carga de varios archivos

    Del mismo modo, podemos escribir c贸digo para cargar varios archivos. Agregue el siguiente mapeo en FileController.java:

    @PostMapping("/uploadFiles")
    public String uploadFiles(@RequestParam("files") MultipartFile[] files, RedirectAttributes redirectAttributes) {
    
        Arrays.asList(files)
            .stream()
            .forEach(file -> fileService.uploadFile(file));
    
        redirectAttributes.addFlashAttribute("message",
            "You successfully uploaded all files!");
    
        return "redirect:/";
    }
    

    Como puede ver, el /uploadFiles El mapeo es similar al anterior, excepto que tiene un MultipartFile[] como argumento. Usamos la API de Java 8 Stream para cargar cada archivo en la matriz.

    Al igual que antes, si la operaci贸n anterior es exitosa, configuramos el mensaje de 茅xito en redirectAttributes y redirigir a la misma p谩gina.

    Ahora, solo necesitamos actualizar el c贸digo en la plantilla upload.html para manejar esto:

    <h4>Upload Multiple Files:</h4>
    <form method="POST" th:action="@{/uploadFiles}" enctype="multipart/form-data">
      <input type="file" name="files" multiple/> <br/><br/>
      <button type="submit">Submit</button>
    </form>
    
    <hr/>
    

    Lo 煤nico diferente del HTML anterior es que el mapeo es al /uploadFiles punto final y el input tiene un multiple atributo, lo que le permite seleccionar m谩s de un archivo. Tambi茅n desde el @RequestParam es files, tenemos que usar el mismo name en HTML.

    Ejecutemos nuestra aplicaci贸n nuevamente y naveguemos a http://localhost:8080:

    Elegir la segunda opci贸n ahora nos permite seleccionar m谩s de un archivo de nuestro sistema de archivos y cargarlos todos.

    Limitar el tama帽o del archivo

    Puede ajustar los l铆mites de carga de archivos utilizando spring.servlet.multipart.max-file-size y spring.servlet.multipart.max-request-size en application.properties:

    spring.servlet.multipart.max-file-size = 5MB
    spring.servlet.multipart.max-request-size = 5MB
    

    Puedes establecer los l铆mites en KB, MB, GBetc.

    El valor predeterminado para spring.servlet.multipart.max-file-size es 1 MB y el valor predeterminado para spring.servlet.multipart.max-request-size es de 10 MB. Aumentando el l铆mite para max-file-size Probablemente sea una buena idea ya que el valor predeterminado es muy bajo, pero tenga cuidado de no establecerlo demasiado alto, lo que podr铆a sobrecargar su servidor.

    Conclusi贸n

    En este art铆culo, hemos cubierto c贸mo cargar un solo archivo y varios archivos en una aplicaci贸n Spring Boot. Usamos Spring’s MultipartFile interfaz para capturar el archivo en la solicitud HTTP y las plantillas Thymeleaf como nuestro motor de renderizado.

    Como siempre, el c贸digo de los ejemplos utilizados en este art铆culo se puede encontrar en GitHub.

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *