5 Replies Latest reply on Jul 2, 2009 11:33 PM by Francois Eric

    Serializing large CSV file

    Francois Eric Novice

      Hello,


      I would like to serialize a large object tree to a csv without busting my memory.


      My object tree is a map that contains approximately 1,000,000 objects.  Today, I iterate through the object tree and stock everything in a StringBuffer.  I then send this buffer content to my JSF page:




      <ui:composition xmlns="http://www.w3.org/1999/xhtml"
                      xmlns:s="http://jboss.com/products/seam/taglib"
                      xmlns:ui="http://java.sun.com/jsf/facelets"
                      xmlns:f="http://java.sun.com/jsf/core"
                      xmlns:h="http://java.sun.com/jsf/html"
                      xmlns:rich="http://richfaces.org/rich"
                      xmlns:a="http://richfaces.org/a4j"> 
          <h:messages styleClass="message" infoClass="success" errorClass="error" warnClass="warning"/>
      
          <f:view contentType="text/csv">#{reportTemplateAction.reportContent}</f:view>
      </ui:composition>



      Obviously this is causing me to have twice the representation of the object in memory at once and causing me memory issues.


      Is there any way I can serialize this with JSF and seam while iterating through it?


      Thanks



        • 1. Re: Serializing large CSV file
          Nikos Paraskevopoulos Novice

          Hello,


          I have done something similar: A link that generates a file, then serves its content to the client. In other words a standard download link. NOTE: my use case does not submit the form. The data is gathered in a wizard and after finishing it, the user is provided with a link to download the generated document. Your code seems to contain a messages component, so it may be different use case. Anyway, here is how:


          First you need a link that calls an action:


          <s:link value="..." action="#{generationBean.download}" />
          



          In the action method:


          @Name("generationBean")
          @AutoCreate
          @Scope(EVENT)
          public class GenerationBean {
               ...
               public void download() {
                    ...
                    // watch out if it may run in portal!!!
                    HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
                    response.setContentType(...as appropriate...);
                    response.addHeader("Content-disposition", "attachment; filename=\"THE FILENAME HERE\"");
          
                    try {
                         ServletOutputStream os = response.getOutputStream();
                         os.write(...data...); // ITERATE OVER YOUR STRUCTURE AND WRITE DATA, A PIECE AT A TIME
                         // OR GIVE THE ServletOutputStream TO ANYTHING THAT WRITES TO STREAMS
                         os.flush();
                         os.close();
                         FacesContext.getCurrentInstance().responseComplete(); // DONT FORGET THIS ONE
                    }
                    catch(IOException ioe) {
                         ...
                    }
               }
               ...
          }
          



          The browser sees the content type and acts accordingly. The JSF mechanism obeys the responseComplete() command and does not execute the render response phase.

          • 2. Re: Serializing large CSV file
            Francois Eric Novice

            Awesome.  Exactly what I was looking for.


            Thanks

            • 3. Re: Serializing large CSV file
              Francois Eric Novice

              Works well but the only thing is that the attachment is not downloadable but inserted directly in browser.  I would like to have a popup where the user chooses to save or open the attachement.  My code is the following:


                          HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
                          response.setContentType("text/csv");
                          response.addHeader("Content-disposition", "attachment; filename=\"" + fileName + "\"");
                           
                          if(reportTemplate.getIsDaily()) {
                              facadeLocator.getReportTemplateGeneratorDailyFacade().generateCSVReport(reportTemplate, response.getOutputStream());
                          } else {
                              facadeLocator.getReportTemplateGeneratorHourlyFacade().generateCSVReport(reportTemplate, response.getOutputStream());
                          }
                          response.getOutputStream().flush();
                          response.getOutputStream().close();
                          FacesContext.getCurrentInstance().responseComplete()
              



              where the content is generated by my methods.


              How can I force the attachment (even though I have set it in the header)?


              Thanks


              Francois

              • 4. Re: Serializing large CSV file
                Cody Lerum Apprentice

                It's a function of the clients browser.


                IF you want it downloaded then response.setContentType(application/octet-stream);


                Otherwise specify the correct mime type (text/csv) and if clients browser can handle the type then it will be loaded in the browser.

                • 5. Re: Serializing large CSV file
                  Francois Eric Novice

                  Thanks Cody.


                  Actually the problem was even simpler.  I was using a a4j:commandButton for my download link.  By using a h:commandButton it solved my problem and now works flawlessly.


                  Thanks again to both of you.