Subir archivos al servidor

Al momento de crear una aplicación web es normal que nos encontremos con la necesidad de almacenar archivos en el servidor, en este tutorial veremos cómo Spring MVC soporta la subida de archivos, en este ejemplo admitiremos la subida de imágenes que serán almacenadas en la base de datos y que luego recuperaremos para mostrarlas en la página correspondiente.

Es importante mencionar que existen servicios externos que podemos utilizar para almacenar todo tipo de archivos, debes saber que guardar archivos de gran tamaño en la BD no suele ser buena práctica, lo recomendable es guardar los archivos en un servidor externo y almacenar en la BD solo el enlace al mismo, guardar imágenes en la base de datos puede ser viable si las imágenes son pequeñas, una foto de perfil por ejemplo.

Subir archivos con Spring Web

Lo primero que haremos será añadir la dependencia commons-fileupload, además de las requeridas para utilizar el framework Spring, para la base de datos usaremos HSQLDB.

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>

Ahora podemos configurar el MultipartResolver con él indicamos el directorio temporal en donde se guardarán los archivos, el tamaño máximos permitido, etc., en nuestro caso los archivos nos se guardarán en ninguna carpeta del servidor, si no en la base de datos como un tipo BLOB.

@EnableWebMvc
@Configuration
@ComponentScan("carmelo.spring.controller")
public class WebAppConfig extends WebMvcConfigurerAdapter {

    @Bean
    public MultipartResolver multipartResolver() throws IOException {
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
        multipartResolver.setMaxUploadSize((1024 * 1024) * 10);
        return multipartResolver;
    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/views/", ".jsp");
    }
}

Nuestra configuración solo establece el tamaño máximo de archivos a 10 MB, si un archivo supera este tamaño se producirá una excepción, este valor debemos indicarlo en bytes.

Para definir la página JSP solo debemos agregar un formulario al cual le cambiamos en atributo enctype a multipart/form-data, también agregamos un input de tipo file que le permite al usuarios abrir un cuadro de dialogo en donde localizar el archivo a subir.

<form method="post" action="/upload/form" enctype="multipart/form-data">
    <input type="file" name="file"/>
    <input type="submit" value="Subir archivo"/>
</form>

Para manejar los archivos subidos mediante este formulario requerimos el siguiente método en nuestro controlador:

@RequestMapping(path = "/form", method = RequestMethod.POST)
public String handleFormUpload(
        @RequestParam("file") MultipartFile file) throws IOException {

    if (!file.isEmpty()) {

        String sql = "INSERT INTO imagen (nombre, tipo, tamano, pixel) VALUES(?, ?, ?, ?)";

        String nombre = file.getOriginalFilename();
        String tipo   = file.getContentType();
        Long tamano   = file.getSize();
        byte[] pixel  = file.getBytes();

        jdbc.update(sql, nombre, tipo, tamano, pixel);
    }

    return "redirect:/";
}

El objeto MultipartFile contiene toda la información respectiva al archivo, obtenemos su nombre, tipo, tamaño y datos que lo conforman para enviarlos a la base de datos, la tabla esta definida de la siguiente manera, con el lenguaje HSQLDB:

CREATE TABLE imagen(nombre VARCHAR(100), tipo VARCHAR(10), tamano BIGINT, pixel BLOB(10M))

La columna en donde almacenamos las imágenes es de tipo BLOB que nos sirve para almacenar un conjunto de bytes, hemos establecido en tamaño máximo a 10 MB.

Usando file.getBytes() obtenernos el arreglo de bytes que conforman el archivo, esto es lo que guardaremos en la columna de tipo BLOB, para enviar a la BD usamos JdbcTemplate para enviar las respectivas consultas.

Visualizar Imagen de la Base de datos

La pregunta en este momento es, cómo visualizar en mi página JSP la imagen almacenada en la base de datos, para hacer esto crearemos un método que maneje las peticiones a la URL con esta forma: /upload/uploaded/?nombre=imagen.jpg, el parámetro nombre indica la imagen que deseamos obtener, veamos el código:

@RequestMapping(value = "/uploaded")
public void getUploadedPicture(
        @RequestParam("nombre") String nombre, HttpServletResponse response)
        throws IOException {

    String sql = "SELECT pixel, tipo FROM imagen WHERE nombre = '" + nombre + "'";
    List<Map<String, Object>> result = jdbc.queryForList(sql);

    if (!result.isEmpty()) {
        byte[] bytes = (byte[]) result.get(0).get("PIXEL");
        String mime = (String) result.get(0).get("TIPO");

        response.setHeader("Content-Type", mime);
        response.getOutputStream().write(bytes);
    }
}

Como podemos ver lanzamos una consulta que nos devuelve el tipo y los bytes que componen la imagen, el truco esta en usar el objeto HttpServletResponse para escribir la imagen en la respuesta a la petición, debemos cambiar el Content-Type y escribir los bytes.

response.setHeader("Content-Type", mime);
response.getOutputStream().write(bytes);

De este modo podemos hacer referencia a una imagen de este modo:

<img src="/upload/uploaded?nombre=${nombre}" />

Ejecutando lo que tenemos hasta ahora, la página inicial se muestra de esta forma:

Subir archivos al servidor con Spring Web

En la parte inferior se puede ver la lista de las imágenes subidas y almacenadas en la base de datos, para crear dicha lista consultamos la BD y enviamos el atributo correspondiente al modelo para que este pudiese ser mostrado desde la vista JSP, el código no se muestra, pues ya lo hemos visto en tutoriales anteriores.

Al hacer clic en el enlace de cada imagen, este nos envía a la página image.jsp pasándole como parámetro el nombre de la imagen que deseamos visualizar, por ejemplo, el enlace para la primera imagen seria: /upload/image?nombre=lena.jpg.

La petición realizada mediante el enlace previamente mencionado sería manejada por el siguiente método:

@RequestMapping("/image")
public ModelAndView image(@RequestParam("nombre") String nombre) {
    return new ModelAndView("image", "nombre", nombre);
}

Este se encarga de invocar la vista y pasarle el nombre de la imagen como un atributo del modelo.

/WEB-INF/views/image.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Image View</title>
    </head>
    <body>
        <h1>Visor de Imagen</h1>
        <h4>Mostrando la imagen: <i>${nombre}</i></h4>
        <img src="/upload/uploaded?nombre=${nombre}" />
    </body>
</html>

Mostrar imagen almacenada en base de datos

En futuros tutoriales aprenderemos a enviar el archivo recibido a un servidor especializado en el almacenamiento de archivo, como Google Drive, Dropbox, etc., hasta la próxima.

Descargar proyecto: spring-subir-archivos.zip

Comentarios

Entradas populares de este blog

Conectar SQL Server con Java

Entrenar OpenCV en Detección de Objetos

Acceso a la webcam con OpenCV

JavaFx 8 Administrar ventanas

Analizador Léxico