Exportation de Données en Fichier Excel via Apache POI

L'exportation de données vers des fichiers au format Excel est une fonctionnalité récurrente dans les applications web. Ce guide propose une implémentation de cette capacité en s'appuyant sur la bibliothèque Apache POI côté serveur Java, orchestrée par une interface utilisateur simple en HTML et JavaScript pour initier le téléchargement.

Interface Client (HTML et JavaScript)

Le processus commence sur le client, où un bouton déclenche l'envoi des données à exporter au serveur. Une liste d'objets est convertie en une chaîne JSON, puis cette chaîne est encodée pour être intégrée en tant que paramètre d'URL lors de l'appel au contrôleur Java.


<!-- Structure HTML pour le bouton d'exportation -->
<div class="btn-group">
    <button class="btn sbold green" id="boutonExport" type="button">
        <span class="ladda-label">Exporter en Excel</span>
    </button>
</div>

<!-- Script JavaScript pour la logique d'exportation -->
<script type="text/javascript">
    // Exemple de données à exporter, simulant une liste d'objets provenant du backend
    const listeElementsAExporter = [
        { id: 1, codeContrat: "CTR-001", nomElement: "Serveur Pro", codeElement: "SERV-A1", nomCategorie: "Informatique", codeCategorie: "INFO", nomUtilisateur: "Alice Dupont", telephoneUtilisateur: "0612345678", typeDocument: "Passeport", numeroDocument: "P123456", dateCreation: "2023-01-15T10:00:00.000Z" },
        { id: 2, codeContrat: "CTR-002", nomElement: "Logiciel Suite", codeElement: "SW-005", nomCategorie: "Logiciels", codeCategorie: "SW", nomUtilisateur: "Bob Martin", telephoneUtilisateur: "0787654321", typeDocument: "CNI", numeroDocument: "CNI98765", dateCreation: "2023-02-20T14:30:00.000Z" },
        { id: 3, codeContrat: "CTR-003", nomElement: "Formation Data", codeElement: "FORM-DB", nomCategorie: "Services", codeCategorie: "SERV", nomUtilisateur: "Charlie Leblanc", telephoneUtilisateur: "0666554433", typeDocument: "Permis", numeroDocument: "PERM321", dateCreation: "2023-03-01T09:15:00.000Z" }
    ];

    $("#boutonExport").on("click", function() {
        const chaineJsonDonnees = JSON.stringify(listeElementsAExporter);
        // 'cheminBaseApplication' doit être une variable globale ou déterminée dynamiquement
        const cheminBaseApplication = '/votre_application_context'; // Ajustez selon votre environnement
        window.location.href = cheminBaseApplication + "/export/telechargerExcel?donneesAExporter=" + encodeURIComponent(chaineJsonDonnees);
    });
</script>

La fonction JavaScript encodeURIComponent() est essentielle. Elle garantit que les caractères spéciaux contenus dans la chaîne JSON sont correctement encodés pour être transmis via une URL sans risque de corruption des données.

Gestionnaire d'Exportation Côté Serveur (Java Spring)

Sur le serveur, une méthode d'un contrôleur Spring reçoit la chaîne JSON encodée. Elle est ensuite décodée et transformée en une liste d'objets Java. La bibliothèque Apache POI est utilisée pour la création du fichier Excel. Pour la gestion efficace de grands ensembels de données et la prévention des erreurs de mémoire, nous utilisons SXSSFWorkbook, qui optimise la mémoire en écrivant une partie des données sur le disque.


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import jakarta.servlet.http.HttpServletResponse; // Ou javax.servlet.http.HttpServletResponse pour les anciennes versions de Spring
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

// Classe de modèle pour les données à exporter
class DonneeExportable {
    private int id;
    private String codeContrat;
    private String nomElement;
    private String codeElement;
    private String nomCategorie;
    private String codeCategorie;
    private String nomUtilisateur;
    private String telephoneUtilisateur;
    private String typeDocument;
    private String numeroDocument;
    private Date dateCreation;

    // Constructeur par défaut et getters/setters (omis pour concision)
    public DonneeExportable() {}

    public DonneeExportable(int id, String codeContrat, String nomElement, String codeElement, String nomCategorie, String codeCategorie, String nomUtilisateur, String telephoneUtilisateur, String typeDocument, String numeroDocument, Date dateCreation) {
        this.id = id;
        this.codeContrat = codeContrat;
        this.nomElement = nomElement;
        this.codeElement = codeElement;
        this.nomCategorie = nomCategorie;
        this.codeCategorie = codeCategorie;
        this.nomUtilisateur = nomUtilisateur;
        this.telephoneUtilisateur = telephoneUtilisateur;
        this.typeDocument = typeDocument;
        this.numeroDocument = numeroDocument;
        this.dateCreation = dateCreation;
    }

    // Getters
    public int getId() { return id; }
    public String getCodeContrat() { return codeContrat; }
    public String getNomElement() { return nomElement; }
    public String getCodeElement() { return codeElement; }
    public String getNomCategorie() { return nomCategorie; }
    public String getCodeCategorie() { return codeCategorie; }
    public String getNomUtilisateur() { return nomUtilisateur; }
    public String getTelephoneUtilisateur() { return telephoneUtilisateur; }
    public String getTypeDocument() { return typeDocument; }
    public String getNumeroDocument() { return numeroDocument; }
    public Date getDateCreation() { return dateCreation; }

    // Setters (omis pour concision)
    public void setId(int id) { this.id = id; }
    public void setCodeContrat(String codeContrat) { this.codeContrat = codeContrat; }
    public void setNomElement(String nomElement) { this.nomElement = nomElement; }
    public void setCodeElement(String codeElement) { this.codeElement = codeElement; }
    public void setNomCategorie(String nomCategorie) { this.nomCategorie = nomCategorie; }
    public void setCodeCategorie(String codeCategorie) { this.codeCategorie = codeCategorie; }
    public void setNomUtilisateur(String nomUtilisateur) { this.nomUtilisateur = nomUtilisateur; }
    public void setTelephoneUtilisateur(String telephoneUtilisateur) { this.telephoneUtilisateur = telephoneUtilisateur; }
    public void setTypeDocument(String typeDocument) { this.typeDocument = typeDocument; }
    public void setNumeroDocument(String numeroDocument) { this.numeroDocument = numeroDocument; }
    public void setDateCreation(Date dateCreation) { this.dateCreation = dateCreation; }
}

@Controller
public class ExportationFichierController {

    @RequestMapping("/export/telechargerExcel")
    public void telechargerFichierExcel(@RequestParam("donneesAExporter") String donneesJson, HttpServletResponse reponseHttp) throws IOException {
        try {
            // Utilisation de Jackson pour parser la chaîne JSON en liste d'objets Java
            ObjectMapper mapper = new ObjectMapper();
            List<DonneeExportable> listeElements = mapper.readValue(donneesJson, new TypeReference<List<DonneeExportable>>(){});

            if (listeElements == null || listeElements.isEmpty()) {
                reponseHttp.sendError(HttpServletResponse.SC_NO_CONTENT, "Aucune donnée fournie pour l'exportation.");
                return;
            }

            // Création d'un classeur Excel avec SXSSFWorkbook (pour les grands ensembles de données)
            // Le paramètre de 1000 indique que 1000 lignes seront conservées en mémoire avant d'écrire sur disque.
            SXSSFWorkbook classeur = new SXSSFWorkbook(1000);
            SXSSFSheet feuille = classeur.createSheet("Rapport d'Exportation");

            // Définition des largeurs de colonne
            feuille.setColumnWidth(0, 15 * 256); // ID
            feuille.setColumnWidth(1, 25 * 256); // Code Contrat
            feuille.setColumnWidth(2, 30 * 256); // Nom Élément
            feuille.setColumnWidth(3, 20 * 256); // Code Élément
            feuille.setColumnWidth(4, 25 * 256); // Nom Catégorie
            feuille.setColumnWidth(5, 20 * 256); // Code Catégorie
            feuille.setColumnWidth(6, 25 * 256); // Nom Utilisateur
            feuille.setColumnWidth(7, 20 * 256); // Téléphone
            feuille.setColumnWidth(8, 20 * 256); // Type Document
            feuille.setColumnWidth(9, 25 * 256); // Numéro Document
            feuille.setColumnWidth(10, 20 * 256); // Date Création

            // Création de la ligne d'en-tête
            SXSSFRow ligneEntete = feuille.createRow(0);
            String[] entetes = {"ID", "Code Contrat", "Nom Élément", "Code Élément", "Nom Catégorie", "Code Catégorie",
                                "Nom Utilisateur", "Téléphone", "Type Document", "Numéro Document", "Date Création"};
            for (int i = 0; i < entetes.length; i++) {
                ligneEntete.createCell(i).setCellValue(entetes[i]);
            }

            // Remplissage des données
            int indexLigne = 1; // Commence à la deuxième ligne après l'en-tête
            SimpleDateFormat formatteurDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            for (DonneeExportable element : listeElements) {
                SXSSFRow ligneDonnees = feuille.createRow(indexLigne++);
                ligneDonnees.createCell(0).setCellValue(element.getId());
                ligneDonnees.createCell(1).setCellValue(element.getCodeContrat());
                ligneDonnees.createCell(2).setCellValue(element.getNomElement());
                ligneDonnees.createCell(3).setCellValue(element.getCodeElement());
                ligneDonnees.createCell(4).setCellValue(element.getNomCategorie());
                ligneDonnees.createCell(5).setCellValue(element.getCodeCategorie());
                ligneDonnees.createCell(6).setCellValue(element.getNomUtilisateur());
                ligneDonnees.createCell(7).setCellValue(element.getTelephoneUtilisateur());
                ligneDonnees.createCell(8).setCellValue(element.getTypeDocument());
                ligneDonnees.createCell(9).setCellValue(element.getNumeroDocument());
                ligneDonnees.createCell(10).setCellValue(element.getDateCreation() != null ? formatteurDate.format(element.getDateCreation()) : "");
            }

            // Configuration des en-têtes de la réponse HTTP pour le téléchargement du fichier
            String nomFichierFinal = "Rapport_Export_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + ".xlsx";
            // L'encodage 'UTF-8'' dans 'filename*' assure la compatibilité avec les caractères non-ASCII
            reponseHttp.setHeader("Content-Disposition", "attachment; filename=\"" + nomFichierFinal + "\"; filename*=UTF-8''" + nomFichierFinal);
            reponseHttp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); // MIME type pour les fichiers .xlsx

            // Écriture du classeur Excel dans le flux de sortie de la réponse HTTP
            try (OutputStream fluxSortie = reponseHttp.getOutputStream()) {
                classeur.write(fluxSortie);
                fluxSortie.flush();
            } finally {
                // Il est crucial de libérer les ressources temporaires allouées par SXSSFWorkbook
                classeur.dispose();
            }

        } catch (JsonProcessingException e) {
            // Gérer les erreurs de parsing JSON
            reponseHttp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Format JSON des données à exporter invalide.");
            e.printStackTrace();
        } catch (Exception e) {
            // Gérer toute autre exception survenant durant le processus d'exportation
            reponseHttp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Une erreur inattendue est survenue lors de la génération du fichier Excel.");
            e.printStackTrace();
        }
    }
}

Cette approche permet la génération dynamique de fichiers Excel à partir de données client, offrant une solution robuste et optimisée pour l'exportation de rapports ou de listes volumineuses dans une application web.

Étiquettes: Apache POI Excel Java Spring MVC JavaScript

Publié le 24 septembre à 00h11