Générer PDF en Spring

Construire un PDF à partir du HTML avec XhtmlRenderer 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

A travers l'article consacré à des contrôleurs dans Spring, on a pu voir que les pages web ne sont pas une seule réponse possible retournée par les contrôleurs. Entre autres, une chaîne de caractères en format JSON peut être affichée grâce aux message converters. Cependant comment faire avec d'autres types, comme les fichiers à télécharger (PDF, Excel, Word...) ?

Dans notre application on utilisera cette fonctionnalité pour deux actions. La première est l'export d'un livre au format PDF. L'autre option est l'export d'une liste d'emprunts en format Excel. Pour les deux on utilisera les librairies externes.

Comment retourner un fichier en Java ?

La gestion des réponses dans les contrôleurs du Spring ne diffère guère de celle à l'aide de la méthode header() en PHP. Il suffit d'indiquer dans l'en-tête le type du fichier renvoyé ainsi une définition rendant possible le téléchargement du fichier préparé. Voici les deux en-têtes :

response.setHeader("Content-Disposition", "inline; filename=" + fileName);
response.setContentType("application/vnd.ms-excel");

response est l'instance de l'implémentation de l'interface javax.servlet.http.HttpServletResponse. La méthode setHeader() permet de spécifier un en-tête. Le premier paramètre indique le nom de l'en-tête et le second sa valeur. Concernant la fonction setContentType(), elle permet de spécifier le type de la réponse renvoyée.

Renvoyer un PDF en Spring

Dans la génération des PDFs on utilisera le projet Flying Saucer qui permet de transformer les pages HTML en fichiers PDF. Pour accomplir cette tâche, il emploie les librairies externes : iText, TagSoup et Jtidy.

La mise en place du Flying Saucer (anciennement Xhtmlrenderer) est très intuitive. La première chose à faire consiste à spécifier les réponses retournées. Ensuite on doit créer l'instance de la classe javax.servlet.ServletOutputStream à partir de HttpServletResponse. Cette instance se charge de produire une réponse contenant des données binaires. Ensuite il nous suffit de rajouter un lien vers la page qui doit être transformée en PDF et attendre le résultat. Voici le code qui illustre ces propos :

        ServletOutputStream outputStream = null;
        try  {
            String url = UtilitaryTool.getBaseUrl(request)+"/books/x/y/"+bookId+"?pdf=true";
            logger.info("Generating url " + url);
            // Debug only
            // InputStream input = new URL(url).openStream();
            // StringWriter writer = new StringWriter();
            // CopyUtils.copy(input, writer);
            // logger.info("Found : " + writer.toString());

            response.setContentType("application/pdf; charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=report.pdf");
            outputStream = response.getOutputStream();

            ITextRenderer renderer = new ITextRenderer();
            renderer.setDocument(url);
            renderer.layout();
            renderer.createPDF(outputStream);
        } catch (Exception e)  {
            logger.error("An error occured on sending PDF to browser", e);
        } finally {
            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

Comment exécuter son code dans le listener ?

Pour ce faire il suffit de surcharger la méthode handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) qui se trouve dans AppKernel.php .

Une nouvelle méthode handle() peut se présenter ainsi :

function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true)
{
  echo 'handle request';

  return parent::handle($request, $type, $catch);
}