POI Excel IllegalStateException getOutputStream()

Bonjour a tous,

J’ai besoin de renvoyer un fichier excel de mon controlleur a la JSP.
Pour ça j’utilise le POI HSSFWorkbook.

Le fichier ce remplit bien et demande bien a l’utilisateur d’enregistrer le fichier excel.

Mais apres il m’affiche un message d’erreur que je ne comprend pas :

Erreur :

GRAVE: “Servlet.service()” pour la servlet jsp a lancé une exception
java.lang.IllegalStateException: “getOutputStream()” a déjà été appelé pour cette réponse

Voici mon code :

c’est lorsque je fais le wb.write(response.getOutputStream());
que ca m’affiche l’erreur.

HSSFWorkbook wb;
HSSFSheet sheet;
HSSFRow row;
HSSFCell cell;

wb = new HSSFWorkbook();
sheet = wb.createSheet("erreur");

response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment; filename=erreur.xls");

row = sheet.createRow((short) ligneErreur++);
cell = row.createCell((short) 0);
cell.setCellValue("message");

    wb.write(response.getOutputStream()); // wb objet HSSFWorkBook de POI


response.getOutputStream().close();

Quelq’un peut m’aider ? :slight_smile:

Merci

Il est interdit de modifier l’entete de ton flux HTTP de sortie après que du contenu y ait été inséré.

Concrètement, ca veut dire que ton programme ou bien un précédent ait appelé HttpServletResponse.getOutputStream() avant un HttpServletResponse.setContentType ou setHeader.

Apparement, pas de probleme ici. Par contre, regarde s’il a pas été appelé par un Controller précédent + forward, ou bien si ta requete n’est pas sous la cible d’un filtre de Servlet (dans ton web.xml : ) qui modifie HttpServletResponse.outputStream. Typiquement, il y a des architectures logicielles qui placent un filtre pour spécifier l’encoding character à UTF8.

Merci mais j’ai regarder partout je ne fais jamais appel à getOutputStream() avant.
ni dans le web.xml
Il y a moyen de mettre quelque chose avant le HttpServletResponse.setContentType pour le reinitialiser ?
merci

Aucun mapping de filtre de servlet dans ton web.xml ?
Ton traitement est appelé directement ?

Si tu réponds Non et Oui, je te propose de coller toute ta classe.

Si il y a des mappings mais je comprends pas comment faire :

Voila mon web.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns=“http://java.sun.com/xml/ns/javaee
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
java.sun.com…
version=“2.5”>

<filter>
    <filter-name>Hibernate Session In View Filter</filter-name>
    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
    <init-param>
        <param-name>sessionFactoryBeanName</param-name>
        <param-value>sessionFactory</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>Hibernate Session In View Filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
    <filter-name>Spring character encoding filter</filter-name>

    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>Spring character encoding filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>


<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
</servlet>


<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>*.html</url-pattern>
</servlet-mapping>


<context-param>
    <param-name>PARAMETER_ENCODING</param-name>
    <param-value>UTF-8</param-value>
</context-param>

<context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>


<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

<welcome-file-list>
    <welcome-file>accueil.html</welcome-file>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.jsp</welcome-file>
</welcome-file-list>

<error-page>
    <exception-type>java.lang.Exception</exception-type>
    <!-- Displays a stack trace -->
    <location>/WEB-INF/jsp/erreurInattendue.jsp</location>
</error-page>

<resource-ref>
    <description>PostgreSQL - GRA_DS</description>
    <res-ref-name>jdbc/GRA_DS</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
</resource-ref>

et voila ma classe :

public class FileUploadController extends SimpleFormController {

Statement deposition = null;
static int idReleve = 30000;
static Integer idEmploye = 0;
static int idPrestation = 1000;
static int idTyprePrestation = 0;

static WritableWorkbook workbookOut = null;

static int ligneErreur = 0; //numéro de ligne du fichier erreur
static WritableSheet sheet2 = null;

String moisSelectionne = null;
String anneeSelectionne = null;

boolean aErreur = false;
final int TAILLE_MATRICULE = 10;

protected static final String MESSAGEBOX_INFO = "infoMessage";

HSSFWorkbook wb;
HSSFSheet sheet;
HSSFRow row;
HSSFCell cell;


@Autowired
private AppService service;

private String nomFichierErreur2;


@RequestMapping("/uploadFile.html")
public String ajouterBD(Model model, HttpServletRequest httpRequest, HttpServletResponse response) throws IOException {

    MultipartHttpServletRequest request = (MultipartHttpServletRequest) httpRequest;

    MultipartFile file = request.getFile("fichier");


    wb = new HSSFWorkbook();
    sheet = wb.createSheet("erreur");


    response.setContentType("application/vnd.ms-excel");
    response.setHeader("Content-Disposition", "attachment; filename=erreur.xls");

    row = sheet.createRow((short) 0);
    cell = row.createCell((short) 0);


    if (file != null) {

        System.out.println("contentType: " + file.getContentType());
        System.out.println("size: " + file.getSize());
        System.out.println("fileName: " + file.getOriginalFilename());

        aErreur = false;
        request.getSession().setAttribute("cheminFichierErreur", null);

        seConnecter();


        ligneErreur = 0; //numéro de ligne du fichier erreur
        String nomFichierErreur = "//One2/Service-INFO/COMMUN/SOURCES/Gra/importation/RMA/Erreur/Erreur-RMA.xls";
        nomFichierErreur2 = "file://One2/Service-INFO/COMMUN/SOURCES/Gra/importation/RMA/Erreur/Erreur-RMA.xls";


        // ouverture du fichier d'erreur
        try {
            workbookOut = Workbook.createWorkbook(new File(nomFichierErreur));
            sheet2 = workbookOut.createSheet("Erreur-RMA", 0); // nom de l'onglet 0
        } catch (IOException e) {
            System.out.println("erreur d'ouverture du fichier d'erreur");
        }

        if (file.getOriginalFilename().equals("")) {
            //ajouterCell(sheet2, 0, ligneErreur++, "Vous n'avez pas selectionner de fichier");
            ajouterCell("Vous n'avez pas selectionner de fichier");

            aErreur = true;
        }

        moisSelectionne = (String) request.getParameter("mois");    //récupère le mois choisit par l'utilisateur
        anneeSelectionne = request.getParameter("annee");    //récupère l'année choisit par l'utilisateur

        traiterFichier(file);


    } else {
        System.out.println("Could not find uploaded file");
    }

    // écriture dans le fichier d'erreur
    try {
        try {
            workbookOut.write();
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
    }

    catch (NullPointerException e) {
        System.out.println("erreur workbookOut.write");
    }

    // fermeture du fichier d'erreur
    try

    {
        workbookOut.close();
    }
    catch (WriteException e)

    {
        System.out.println("erreur");
    }
    catch (IOException e)

    {
        System.out.println("erreur");
    }

    catch (NullPointerException e)

    {
        System.out.println("erreur");
    }


    if (aErreur) {
        try {       
            wb.write(response.getOutputStream()); // wb objet HSSFWorkBook de POI
        } catch (IllegalStateException ex) {
            System.out.println("erreur");
        }
        response.getOutputStream().close();


        ajouterMessageInfo(model, "L'insertion dans la base de données n'a pas été effectuée, veuillez consultez le fichier d'erreur");

        request.getSession().setAttribute("cheminFichierErreur", nomFichierErreur2);
    } else

    {
        ajouterMessageInfo(model, "L'insertion dans la base de données à été effectuée sans erreurs");
    }
    return "importRMA";

}

Et ben voilà, c’est clair maintenant :

<filter>
<filter-name>Spring character encoding filter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Spring character encoding filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

Il faut exclure ce filtre de servlet sur le controller FileUploadController. Pour cela :

  • soit tu n’as pas beaucoup d’URL et tu changes le pattern /* en tes différentes adresses sauf FileUploadController
  • soit tu ne peux pas te le permettre et dans ce cas tu peux créer un filtre amont qui va cour circuiter la chaine des filtres
  • soit tu ne peux pas te le permettre et dans ce cas tu peux aussi surcharger ou déléguer le filtre CharacterEncodingFilter

Je ne reviens pas sur la qualité du code qui mériterait une attention bien plus importante …
Edité le 06/10/2009 à 10:37

Je rajouterai que si chaque composant se contenterai de s’occuper de sa fonction sans plus, ce problème ne serait pas apparu :neutre:

C’est le cas. Ici, il s’agit d’un probleme de configuration.