JAVA: 将数据(从数据库)导出到 Excel 并发送到客户端

4
正如标题所示,我需要将从数据库中获取的一些数据放入Excel表格中,然后将其发送到客户端,以便用户可以保存、打开或取消操作。
我看过一些相关文章,最接近的是:如何让用户下载我的文件?(Java,MVC,Excel,POI)。参考Stevens提供的链接,我尝试了以下代码:
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;
}

首先,WorkbookFactory未定义。其次,我无法正确理解代码的工作原理。
我还发现这个链接: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。但是在这里,Excel文件保存在服务器上。我希望文件不要保存在服务器端,而是直接传输到客户端。
(如果有帮助)我正在使用:struts 2框架、hibernate。
我可以使用其他东西,如POI API、jQuery或任何其他好东西。
由于某些原因,我不能使用displayTag
JavaScript将是我的最后一选择(尽管我已经实现了它),因为它需要更改浏览器的某些默认安全设置(如果可以避免,我也可以使用JavaScript)。
请建议我现在该怎么做。
谢谢!
    <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>

执行操作时出错:

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

要理解这段代码,您需要了解POI。WorkbookFactory是POI API的一部分,它是Apache项目。 - Umesh Awasthi
我认为你应该借助临时文件来完成这个任务,使用"File.createTempFile("prefix","suffix")"方法可以创建一个临时文件夹,然后你可以在其中写入和保存内容。在将文件发送到客户端之后,你可以将其删除。 - Prateek Sharma
创建一个独立的项目,使用POI创建带有一些虚拟数据的文档模板。然后在您的容器中执行相同的操作,但现在使用Hibernate获取数据。您应该能够将文件存储在服务器端并检查它(这只是一个测试,您可以将文件保留在内存中)。就Struts2而言,只需发送文件即可。 - Quaternion
@UmeshAwasthi:我发现我没有添加poi-ooxml jar,这是WorkbookFactory错误的原因。其次,现在我已经添加了所需的jar文件,但当我运行项目时出现错误。错误是--> FAIL - 部署应用程序到上下文路径 /,但上下文启动失败....\nbproject\build-impl.xml:770: 该模块尚未部署。构建失败(总时间:5秒)。请参见struts-config .xml文件代码的编辑。 - kanishk
@PrateekSharma:等我把 Excel 表格发送给客户端后,我会尝试一下。 - kanishk
显示剩余11条评论
2个回答

10

好的,最终我终于解决了所有的障碍,并找到了一种方法来做到这一点。

我意识到我面临的问题不在于创建 Excel 文件,而是将其发送到客户端,而且还要在服务器上不创建文件或临时文件的情况下进行。

以下是如何操作的(我从我的原始代码中删除了细节,以便您可以轻松理解它)。

在动作文件中,首先必须创建 HSSFWorkbook 对象,将数据放入其中,然后使用输入流将其发送到客户端,而无需将其保存到服务器磁盘上。

动作文件代码:

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;
}

现在只需在struts-config文件中编写以下内容(请注意,excelStream和contentDisposition已在操作本身中设置,此处的result-type为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>

就这样,当执行该操作时,用户将被提示保存或打开文件。

:)


感谢您的指导...我遇到了一个更严重的问题...我按照您的方法实现了,报告已经成功生成...然而,在Excel文件生成的同时,没有人能够在网站上浏览,直到报告(由其他特定用户请求)完成。 您知道如何解决这个问题吗?我的平台正在生成大约需要10秒钟的报告。 - htobon

2
您的classpath上有两个不同版本的POI,一个是旧版,一个是新版。这就是为什么会出现异常 java.lang.IncompatibleClassChangeError: Class org.apache.poi.hssf.usermodel.HSSFWorkbook does not implement the requested interface org.apache.poi.ss.usermodel.Workbook - 您在新版本下编译,但在运行时,程序发现同时存在新旧两个版本的JAR包。
这个问题在 POI FAQ 中有详细说明。理想情况下,您应该查看设置中的所有JAR文件,并删除旧版的POI。如果不能这样做,POI FAQ入口中提供了一些示例代码,可以让JVM打印出它从哪里加载了POI类。这将显示JAR文件名,您可以删除旧版本的JAR文件。

你说得对,我确实有两个POI的JAR包,但当我改变了编码方式后,错误消失了。可能是在清理和构建项目时被选中的文件发生了变化,因此错误消失了。无论如何,我现在已经删除了旧版本。感谢您的回复! - kanishk
这也正是我遇到的完全相同的问题。感谢您的回复,非常有帮助! - tanghao

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接