4

As the title suggest, I need to put some data (which I have got from database) into an excel sheet, and then send it to client side so that user can save , open or cancel the action.

I have seen some articles regarding this, the closest one being : How can I get the user to download my file? (Java, MVC, Excel, POI). Referring to links provided by Stevens I tried out the following code :

public String execute(){
    setContentDisposition("attachment; filename=\"" + ename + "\"");
    try{
        ServletContext servletContext = ServletActionContext.getServletContext();
        String filePath = servletContext.getRealPath("/WEB-INF/template/excel/mytemplate.xls");
        File file = new File(filePath);
        Workbook wb = WorkbookFactory.create(new FileInputStream(file));
        Sheet sheet = wb.getSheetAt(0);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        wb.write(baos);
        InputStream excelStream;
        excelStream = new ByteArrayInputStream(baos.toByteArray());
    }catch(Exception e){
        System.out.println(e.getMessage());
    }
    return SUCCESS;
}

Here firstly WorkbookFactory is not defined. Secondly, I could not understand properly how the code is working.

I also found this link : http://www.roseindia.net/answers/viewqa/Java-Beginners/14930-How-to-export-data-from-database-to-excel-sheet-by-using-java--in-standalone-project.html. But here the excel file gets saved on the server. I want that the file should not be saved on the server side, it should directly go to client side

(If it helps) I am using : struts 2 framework, hibernate

I am open to using other things like POI API, jQuery or any other good stuff.

I can not use displayTag for some reason.

Javascript would be my last resort (although I have implemented with it) because it requires changing some default security settings of the browser (If this can be avoided I am open to javascript as well).

Please advise how should I go about this now.

Thanks!!

EDIT :

    <result-types>
        <result-type name="jsp" class="org.apache.struts2.views.jsp"/>
        <result-type name="tiles" class="org.apache.struts2.views.tiles.TilesResult"/>
        <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
    </result-types>
    <action name="myActionName" class="package.myActionClass">
         <result type="stream">
            <param name="contentType">"application/vnd.ms-excel"</param>
            <param name="inputName">excelStream</param>
            <param name="contentDisposition">contentDisposition</param>
            <param name="bufferSize">1024</param>
         </result>
    </action>

The error while executing action:

java.lang.reflect.InvocationTargetException

java.lang.IncompatibleClassChangeError: Class org.apache.poi.hssf.usermodel.HSSFWorkbook does not implement the requested interface org.apache.poi.ss.usermodel.Workbook
Community
  • 1
  • 1
kanishk
  • 713
  • 4
  • 15
  • 31
  • for understanding the code you need to know POI.`WorkbookFactory` is a part of POI API which is a Apache project. – Umesh Awasthi Feb 15 '12 at 12:36
  • I think you should do this with the help of temp files "File.createTempFile("prefix","suffix"). It will create a file in temp folder and then u can write and save your content in temp folder. and after sending this file to client side , you can delete this. – Prateek Sharma Feb 15 '12 at 13:14
  • Create a stand alone project which uses POI to create a template of the document with some dummy data. Then in your container do the same but now getting the data with hibernate. You should be able to store the file on the server side and examine it (this is just a test you can keep the file in memory). As far a struts2 is concerned it is just a mater of sending the file. – Quaternion Feb 16 '12 at 05:39
  • @UmeshAwasthi : I found out that I had not added the poi-ooxml jar, the reason for `WorkbookFactory` error. Secondly, now that I have added the required jar file, I am getting an error when I run the project. Error is --> FAIL - Deployed application at context path / but context failed to start....\nbproject\build-impl.xml:770: The module has not been deployed. BUILD FAILED (total time: 5 seconds). See the edit for the struts-config .xml file code. – kanishk Feb 16 '12 at 06:05
  • @PrateekSharma : will try that once I get through sending an excel sheet to clientside. – kanishk Feb 16 '12 at 06:12
  • @Quaternion : please have a look at the struts-config code I have posted in the edit. I am getting an error as mentioned in the previous comments. So I am not able to try out things. – kanishk Feb 16 '12 at 06:24
  • @kanishk: its better to show complete build failure log else its hard to tell anythng – Umesh Awasthi Feb 16 '12 at 06:36
  • @UmeshAwasthi : added the error stack to the edit. – kanishk Feb 16 '12 at 07:00
  • @kanishk: if i read it closely it says that there are error in your `salary.xml` like `Element type "result-type" must be followed by either attribute specifications, ">" or "/>"`please check your `salary.xml` – Umesh Awasthi Feb 16 '12 at 07:03
  • @UmeshAwasthi : As soon as I read the error I checked that out, But I did not find anything, maybe something is escaping my eye. I have posted the parts of the file that I had changed after which this error started. Please have a look and see if you can find something. – kanishk Feb 16 '12 at 07:10
  • @UmeshAwasthi : One more thing, if I comment out the action definition there is no error while building the project. – kanishk Feb 16 '12 at 07:22
  • @UmeshAwasthi : Ok. I found out the error, and it is smaller than I expected it to be. I missed out the quotes in the first param. But unfortunately there is an error in the execution of the action. – kanishk Feb 16 '12 at 07:27
  • @kanishk: i am unable to see any error in your file.just clean the project and than try to build again and see if problem is still there – Umesh Awasthi Feb 16 '12 at 07:32
  • @UmeshAwasthi: I have corrected my code and added the exceptions that I am getting when the action is executed. – kanishk Feb 16 '12 at 07:32
  • @UmeshAwasthi Figured out a way, posted an answer. Is there a better way or some improvements that can be made? – kanishk Feb 16 '12 at 11:46
  • @PrateekSharma Did not require the tempFile thing, infact did not require even file, have a look. – kanishk Feb 16 '12 at 11:47

2 Answers2

10

Okay. So finally I am through with all the roadblocks and have figured out a way to do this.

I realized that the problem I was facing was not in creating the excel file, the problem was sending it to client side, and that too without creating a file or temporary file on the server.

So here is how to go about it (I have ripped off details from my original code so that you can easily understand it).

In the action file you first have to create a HSSFWorkbook object, put data on it and then without saving it to disk on server, send it to client using inputstream.

Action File code :

public String execute(){

    setContentDisposition("attachment; filename=\"" + ename + ".xls\"");

    try{
        HSSFWorkbook hwb=new HSSFWorkbook();
        HSSFSheet sheet =  hwb.createSheet("new sheet");

        //////You can repeat this part using for or while to create multiple rows//////
            HSSFRow row = sheet.createRow(rowNum);
            row.createCell(0).setValue("col0");
            row.createCell(1).setValue("col1");
            row.createCell(2).setValue("col2");
            row.createCell(3).setValue("col3");
            .
            .
            .
        ///////////////////////////////////////////////////////////////////////////////

        ///////////////////////////////////////////////////////////////////////////////
        //////Now you are ready with the HSSFworkbook object to be sent to client//////
        ///////////////////////////////////////////////////////////////////////////////

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        hwb.write(baos);
        excelStream = new ByteArrayInputStream(baos.toByteArray());

        ///////////////////////////////////////////////////////////////////////////////
        ////Here HSSFWorkbook object is sent directly to client w/o saving on server///
        ///////////////////////////////////////////////////////////////////////////////
    }catch(Exception e){
        System.out.println(e.getMessage());
    }
    return SUCCESS;
}

Now in the struts-config file just write (note that excelStream & contentDisposition has been set in the action itself also the result-type here is org.apache.struts2.dispatcher.StreamResult):

    <action name="actionName" class="actionClass">
        <result type="stream">
            <param name="contentType">"application/vnd.ms-excel"</param>
            <param name="inputName">excelStream</param>
            <param name="contentDisposition">contentDisposition</param>
            <param name="bufferSize">1024</param>
        </result>
    </action>

Thats it. Now when the action is executed, the user will be prompted to save or open the file.

:)

kanishk
  • 713
  • 4
  • 15
  • 31
  • Thanks for the guide... I'm having a more serious problem... I implemented exactly in that way and the report is being generated successfully... However, meanwhile the Excel file is being generated, no body can navigate throught the website until the report (requested by other specific user) finalizes. Do you know how to solve thi problem? My platform is making reports that takes around 10 segs to be generated. – htobon Oct 29 '15 at 00:27
2

You have two different copies of POI on your classpath, one old and one new. That's why you're getting the exception java.lang.IncompatibleClassChangeError: Class org.apache.poi.hssf.usermodel.HSSFWorkbook does not implement the requested interface org.apache.poi.ss.usermodel.Workbook - you've compiled against the new copy, but at runtime it's finding some new and some old jars.

This is covered in the POI FAQ. Ideally you should just be able to look at all the jars in your setup, and zap the old POI one. If not, the POI FAQ entry has some sample code you can use to get the JVM to print out where it has loaded POI classes from. That will show you the jar file name, and you can remove the old one.

Gagravarr
  • 47,320
  • 10
  • 111
  • 156
  • You are right that I had 2 POI jars, but still the error went away when I changed the way I was coding. It is possible that the file that was being picked changed when I clean and build the project and therefore the error went away. Anyways I have now deleted the older version. Thanks for replying!! – kanishk Feb 16 '12 at 11:20
  • That's also exactly the same issue I encountered. Thanks for your replying, which is very helpful! – tanghao May 29 '14 at 08:59