Spring MVC Generar vistas y archivos PDF/XLS

El Framework Java Spring MVC nos brinda soporte para manejar gran variedad de vistas, entre ellas: HTML, PDF, XLS, etc., que puedes ser generadas con las tecnologías JSP y Thymeleaf, a las cuáles les hemos dedicado sus respectivos tutoriales, esta vez centraremos nuestro curso en la generación dinámica de archivos PDF y XLS que pueden ser descargados por los usuarios de nuestra aplicación web, utilizaremos la biblioteca iText para la generación de los archivos PDF y Apache POI para generar los archivos XLS de Excel.

Spring MVC Generar archivos PDF y Excel XLS

Esta es la vista JSP de nuestra aplicación de ejemplo, todo los conceptos aplicados ya los hemos visto en tutoriales previos, lo nuevo para nosotros son los enlaces ubicados en la parte inferior de la aplicación, no los enlaces en sí, si no la función que ambos realizan, permitirnos descargar o visualizar la vista en formato PDF o XLS de Excel.

Para nuestro proyecto requerimos las siguientes dependencias:

<!-- Spring Web MVC -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
</dependency>

<!-- Java Servlet y JSP -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>

<!-- PDF View -->
<dependency>
    <groupId>com.lowagie</groupId>
    <artifactId>itext</artifactId>
    <version>2.1.7</version>
</dependency>

<!-- XLS View -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.14</version>
</dependency>

Para configurar el Servlet definiremos dos ViewResolver, estos son los encargados de resolver las vistas a partir del nombre lógico devuelto por el controlador, en este ejemplo definiremos un InternalResourceViewResolver para las vistas JSP y un ResourceBundleViewResolver para las vistas PDF y XLS.

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {
    "carmelo.spring.controller",
    "carmelo.spring.service"})
public class WebAppConfig {

    @Bean
    public ViewResolver resourceBundleViewResolver() {
        ResourceBundleViewResolver viewResolver = new ResourceBundleViewResolver();
        viewResolver.setBasename("views");
        viewResolver.setOrder(1);
        return viewResolver;
    }

    @Bean
    public ViewResolver internalResourceViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        resolver.setOrder(2);
        return resolver;
    }
}

El bean ResourceBundleViewResolver utiliza el archivo views.properties para resolver las vistas, el nombre del archivo a utilizar se puede cambiar usando el método setBasename("..."), usamos el método setOrder(1) para indicar que este será el primer ViewResolver con que debe resolverse la vista, si falla se utilizara el siguiente.

src/main/resources/views.properties

pdfView.(class)=carmelo.spring.view.PdfView
xlsView.(class)=carmelo.spring.view.XlsView

Este archivo nos dice que, cuando el controlador nos devuelva el nombre lógico pdfView debe utilizarse la clase carmelo.spring.view.PdfView para generar la vista, del mismo modo el nombre lógico xlsView debe generar la vista con la clase carmelo.spring.view.XlsView, definiremos ambas clases mas adelante.

Este es nuestro controlador, el método verTodos(Model model) invoca la vista que vemos en la imagen al principio del curso, esta es una vista JSP igual a la hemos visto en todos los cursos anteriores, los demás métodos invocan las respectivas vistas, PDF y XLS.

@Controller
public class CustomerController {

    @Autowired
    private CustomerService cs;

    @ModelAttribute("customerList")
    public List<Customer> getCustomerList() {
        return cs.findAll();
    }

    @RequestMapping("/")
    public String verTodos(Model model) {
        return "customer";
    }

    @RequestMapping("/pdf")
    public String generarPdf(Model model) {
        return "pdfView";
    }

    @RequestMapping("/xls")
    public String generarXls(Model model) {
        return "xlsView";
    }
}

Usamos la anotación @ModelAttribute("...") para añadir un atributo al modelo.

Crear Vista PDF

El método generarPdf(Model model) retorna el nombre lógico pdfView según lo definimos en el archivo views.properties la vista debe generarse con la clase PdfView, esta se define como sigue:

public class PdfView extends AbstractPdfView {

    @Override
    protected void buildPdfDocument(
            Map<String, Object> model,
            Document document,
            PdfWriter writer,
            HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        List<Customer> customerList = (List<Customer>) model.get("customerList");

        PdfPTable table = new PdfPTable(5);

        table.addCell("ID");
        table.addCell("First Name");
        table.addCell("Last Name");
        table.addCell("Street");
        table.addCell("City");

        customerList.forEach(customer -> {
            table.addCell(customer.getId().toString());
            table.addCell(customer.getFirstName());
            table.addCell(customer.getLastName());
            table.addCell(customer.getStreet());
            table.addCell(customer.getCity());
        });

        document.add(table);
    }

}

La clase debe extender AbstractPdfView y sobre-escribir el método buildPdfDocument(...), usaremos la variable model para acceder al atributo que almacenamos en el modelo, luego debemos escribir el código necesario para generar el documento PDF usando la librería iText, no entraremos en detalles sobre el uso de la misma, de manera breve, creamos una tabla en la que añadimos la lista de elementos obtenidos del modelo, usamos document.add(table) para agregar la tabla a nuestro documento PDF.

Crear Vista Excel XLS

El método generarXls(Model model) retorna el nombre lógico xlsView según lo definimos en el archivo views.properties la vista debe generarse con la clase XlsView, esta se define como sigue:

public class XlsView extends AbstractXlsView {

    @Override
    protected void buildExcelDocument(
            Map<String, Object> model,
            Workbook workbook,
            HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        // cambiar nombre de archivo
        response.setHeader("Content-Disposition", "attachment; filename=\"customer.xls\"");

        // crear una pagina excel
        Sheet sheet = workbook.createSheet("Customer List");

        Row header = sheet.createRow(0);

        header.createCell(0).setCellValue("ID");
        header.createCell(1).setCellValue("Firt Name");
        header.createCell(2).setCellValue("Last Name");
        header.createCell(3).setCellValue("Street");
        header.createCell(4).setCellValue("City");

        int count_row = 1;

        for (Customer customer : (List<Customer>) model.get("customerList")) {
            Row customerRow = sheet.createRow(count_row++);

            customerRow.createCell(0).setCellValue(customer.getId());
            customerRow.createCell(1).setCellValue(customer.getFirstName());
            customerRow.createCell(2).setCellValue(customer.getLastName());
            customerRow.createCell(3).setCellValue(customer.getStreet());
            customerRow.createCell(4).setCellValue(customer.getCity());
        }
    }

}

De manera similar a la clase anterior, extendemos la clase AbstractXlsView y sobre-escribimos en método buildExcelDocument(...) en él escribimos en código necesario para generar el documento Excel XLS usando la librería Apache POI.

Si todo es correcto al hacer clic en enlace que genera el archivo PDF tendremos:

Spring MVC generar vista PDF con iText

De modo similar sucederá con el archivo Excel XLS, solo que en mi navegador el archivo se descarga, no se muestra en el navegador, debe ser abierto con un programa externo.

Comentarios

Temas relacionados

Entradas populares de este blog

tkinter Grid

tkinter Canvas

Histogramas OpenCV Python

Python Binance API