Générer Excel en Spring

Renvoyer le document Excel avec Apache POI et Spring

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com

Malgré la possibilité de mise en place des interfaces web riches pouvant suppléer aux tableurs, ces derniers restent encore très populaires. C'est la raison pour laquelle notre application permettra de retourner un fichier Excel. L'article présent montrera comment renvoyer un fichier Excel dans Spring.

Renvoyer un Excel dans Spring

Pour générer nos tableurs Excel on utilisera le projet Apache POI. Il s'agit d'une API qui doit permettre les manipulations sur les fichiers basés sur les standars Office Open XML standards et Microsoft's OLE 2 Compound Document format. Ce projet gère les plus importants formats de la suite Microsoft Office : Word, PowerPoint et Excel. Dans notre cas, on se focalisera uniquement sur ce dernier format.

La construction d'une feuille est très intuitive grâce à des méthodes permettant, entre autres, de définir :
- une ligne : createRow()
- une colonne : createCell()

A part cela, on peut également effectuer des opérations plus complexes, comme concaténation des régions. Le code addMergedRegion(new CellRangeAddress(0,1,0,2)) permet par exemple de concaténer les lignes de 0 à 1 et les colonnes de 0 à 2. Le document peut aussi prendre une mise en forme très riche (taille de police, alignement, type de police...). Tout cela grâce à l'instance de la classe HSSFCellStyle et la méthode setCellStyle() de la classe HSSFCell. Les colonnes et les lignes peuvent bien évidemment prendre une largueur et une hauteur spécifiques via les fonctions setColumnWidth() et setHeight(). Le contenu de chaque cellule est défini avec setCellValue() venant de la classe HSSFCell.

Maintenant qu'on a découvert les fonctionnalités utilisées dans notre application, on peut passer à l'exemple concret :

        if (type.equals(TYPE_EXCEL)) {
            String fileName = "BorrowingReport.xls";
            response.setHeader("Content-Disposition", "inline; filename=" + fileName);
            // Make sure to set the correct content type
            response.setContentType("application/vnd.ms-excel");

            if (source.equals(SOURCE_BORROWINGS)) {
                Map<Long, Map<String, Object>> borrowings = borrowingService.getReportByLangId(lang.getId());
                logger.info("=> Borrowings : " + borrowings);
                HSSFWorkbook workbook = new HSSFWorkbook();
                HSSFSheet worksheet = workbook.createSheet(messageSource.getMessage("borrowing.excel.title", null, Locale.getDefault()));
                worksheet.setColumnWidth(0, 2500);
                worksheet.setColumnWidth(1, 7500);
                worksheet.setColumnWidth(2, 10000);
                
                Font fontTitle = worksheet.getWorkbook().createFont();
                fontTitle.setBoldweight(Font.BOLDWEIGHT_BOLD);
                fontTitle.setFontHeightInPoints((short) 16);
                fontTitle.setUnderline(Font.U_SINGLE);
                HSSFCellStyle cellStyleTitle = worksheet.getWorkbook().createCellStyle();
                cellStyleTitle.setAlignment(CellStyle.ALIGN_CENTER);
                cellStyleTitle.setWrapText(true);
                cellStyleTitle.setFont(fontTitle);
                
                Font fontBody = worksheet.getWorkbook().createFont();
                fontBody.setFontHeightInPoints((short) 14);
                
                short rowsCounter = 0;
                
                HSSFRow rowTitle = worksheet.createRow(rowsCounter);
                rowsCounter++;
                rowTitle.setHeight((short) 700);
                HSSFCell cellTitle = rowTitle.createCell(0);
                cellTitle.setCellValue(messageSource.getMessage("borrowing.excel.title", null, LocaleContextHolder.getLocale()));
                cellTitle.setCellStyle(cellStyleTitle);
                // merge rows and cells : 0, 1 - rows 0 to 1, 0, 2 - cells 0 to 2
		        worksheet.addMergedRegion(new CellRangeAddress(0,1,0,2));
                rowsCounter++;
                
                HSSFCellStyle headerCellStyle = worksheet.getWorkbook().createCellStyle();
                headerCellStyle.setFillBackgroundColor(HSSFColor.GREY_25_PERCENT.index);
                headerCellStyle.setAlignment(CellStyle.ALIGN_CENTER);
                headerCellStyle.setVerticalAlignment(CellStyle.VERTICAL_BOTTOM);
                headerCellStyle.setWrapText(true);
                headerCellStyle.setFont(fontTitle);
                headerCellStyle.setBorderBottom(CellStyle.BORDER_DASHED);
                headerCellStyle.setBottomBorderColor(HSSFColor.GREY_80_PERCENT.index);
                
                HSSFRow rowHeader = worksheet.createRow(rowsCounter);
                rowHeader.setHeight((short) 500);
                
                HSSFCell cell1 = rowHeader.createCell(0);
                cell1.setCellValue(messageSource.getMessage("borrowing.excel.id", null, LocaleContextHolder.getLocale()));
                cell1.setCellStyle(headerCellStyle);

                HSSFCell cell2 = rowHeader.createCell(1);
                cell2.setCellValue(messageSource.getMessage("borrowing.excel.book", null, LocaleContextHolder.getLocale()));
                cell2.setCellStyle(headerCellStyle);

                HSSFCell cell3 = rowHeader.createCell(2);
                cell3.setCellValue(messageSource.getMessage("borrowing.excel.subscriber", null, LocaleContextHolder.getLocale()));
                cell3.setCellStyle(headerCellStyle);
                rowsCounter++;
                for (Map.Entry<Long, Map<String, Object>> entry : borrowings.entrySet()) {
                    Map<String, Object> mapEntry = (Map<String, Object>)entry.getValue();
                    HSSFRow entryRow = worksheet.createRow(rowsCounter);
                    HSSFCell cellId = entryRow.createCell(0);
                    HSSFCell cellBook = entryRow.createCell(1);
                    HSSFCell cellSubscriber = entryRow.createCell(2);
                    
                    Map<String, String> translations = (Map<String, String>)mapEntry.get("translations");
                    logger.info("Got translations " + translations);
                    cellId.setCellValue(((Borrowing)mapEntry.get("borrowing")).getId());
                    cellBook.setCellValue(translations.get("titl"));
                    cellSubscriber.setCellValue(((Subscriber)mapEntry.get("subscriber")).getLogin());
                    
                    rowsCounter++;
                }
                ServletOutputStream outputStream = null;
                try {
                    // Retrieve the output stream
                    outputStream = response.getOutputStream();
                    // Write to the output stream
                    worksheet.getWorkbook().write(outputStream);
                    // Flush the stream
                    // outputStream.flush();
                } catch (Exception e) {
                    logger.error("Unable to write report to the output stream", e);
                } finally {
                    if (outputStream != null) {
                        try {
                            outputStream.flush();
                            outputStream.close();
                        } catch (Exception ex) {
                            logger.error("An error occured on flushing or closing outputStream", ex);
                        }
                    }
                }
            }
        }

Une question ? Une remarque ?

*

*

Un conseil Symfony2

Un problème filemtime()

Si pendant le développement de votre projet Symfony2 vous rencontrez un problème avec fonction filemtime, il peut s'agir d'un dysfonctionnement temporaire. Warning: filemtime() [function.filemtime]: stat failed for C:\Program Files (x86)\EasyPHP-5.3.5.0\www\gagu\src\Bun\DleBundle/Resources/views/Bundle/show.html.php in C:\Program Files (x86)\EasyPHP-5.3.5.0\www\appli\app\cache\dev\classes.php line 2064 Pour résoudre ce problème, vous pouvez être amenés à supprimer le cache du répertoire dev (si vous êtes en mode développement).