如何在 tomcat 上使用 apache POI 3.16 创建 excel 文件?
我正在编写一个 Web 应用程序,它使用 apache POI 3.16 创建一个 XLSX(使用 XSSFWorkbook 类),然后将它简单地写入一个 OutputStream。预期的行为是让用户单击一个按钮,然后将 excel 文件直接写入服务器的响应中,以便用户可以立即下载它。
这在我的开发环境(使用 eclipse 和 jetty)中工作得很好,但是当我使用 maven 创建一个可部署的 .war 文件,并在我的 vanilla Tomcat 上部署它时,我得到了无法解释的错误:
- 只有一张纸的 XLSX 文件可以正常工作。
- 任何超过工作表的文件都会发送错误。
此外,为了尝试解决这个问题,我使用了文档 ( https://poi.apache.org/spreadsheet/quick-guide.html#NewWorkbook ) 中的一个非常基本的示例来编写以下示例:
[...]
//this method is called from the onSubmit of a button being pressed
public void test(){
//test writing a text file
final List<String> lines = Arrays.asList("Line one", "Line two");
final Path filepath = Paths.get("C:/path/to/my/file.txt");
try {
Files.write(filepath, lines, Charset.forName("UTF-8"), StandardOpenOption.CREATE);
} catch (final Exception e) {
InvoiceReportPage.logger.error(e.getMessage(), e);
}
//test writing an excel file
final Workbook wb1 = new XSSFWorkbook();
wb1.createSheet("new sheet");
try {
final FileOutputStream fileOut1 = new FileOutputStream("C:/path/to/my/workbook.xlsx");
wb1.write(fileOut1); //OpenXML4JException in tomcat
fileOut1.close();
} catch (final Exception e) {
InvoiceReportPage.logger.error(e.getMessage(), e);
}
finally {
try {
wb1.close(); //OpenXML4JException in tomcat
} catch(final Exception e) {
InvoiceReportPage.logger.error(e.getMessage(), e);
}
}
}
[...]
这在我的开发 jetty 服务器上工作得很好,但是这一次,无论工作表的数量如何,我都会遇到和以前一样的错误,我最终得到的 XLSX 文件缺少一些内部结构(这可以用 7zip 打开它们可以看到),因此已损坏且无用。
我确保服务器可以读取/写入目录“C:/path/to/my”中的文件,这可以通过文本文件“C:/path/to/my/file”这一事实来证明.txt"已正确写入该文件夹。
这是我在 apache POI 3.16 无法在 tomcat 中创建 excel 文件时得到的错误堆栈跟踪:
org.apache.poi.openxml4j.exceptions.OpenXML4JException: The part /docProps/core.xml fail to be saved in the stream with marshaller org.apache.poi.openxml4j.opc.internal.marshallers.ZipPackagePropertiesMarshaller@2a70f1c7
at org.apache.poi.openxml4j.opc.ZipPackage.saveImpl(ZipPackage.java:582)
at org.apache.poi.openxml4j.opc.OPCPackage.save(OPCPackage.java:1557)
at org.apache.poi.POIXMLDocument.write(POIXMLDocument.java:248)
at com.itp.tomasot.webapp.wicket.InvoiceReportPage$InvoiceReportExcelSubmitLink.onSubmit(InvoiceReportPage.java:115)
at org.apache.wicket.markup.html.form.Form.delegateSubmit(Form.java:1380)
at org.apache.wicket.markup.html.form.Form.onFormSubmitted(Form.java:811)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.wicket.RequestListenerInterface.invoke(RequestListenerInterface.java:183)
at org.apache.wicket.request.target.component.listener.ListenerInterfaceRequestTarget.processEvents(ListenerInterfaceRequestTarget.java:73)
at org.apache.wicket.request.AbstractRequestCycleProcessor.processEvents(AbstractRequestCycleProcessor.java:91)
at com.itp.tomasot.webapp.wicket.WorkflowWebRequestCycleProcessor.processEvents(WorkflowWebRequestCycleProcessor.java:70)
at org.apache.wicket.RequestCycle.processEventsAndRespond(RequestCycle.java:1239)
at org.apache.wicket.RequestCycle.step(RequestCycle.java:1316)
at org.apache.wicket.RequestCycle.steps(RequestCycle.java:1418)
at org.apache.wicket.RequestCycle.request(RequestCycle.java:532)
at org.apache.wicket.protocol.http.WicketFilter.doGet(WicketFilter.java:356)
at org.apache.wicket.protocol.http.WicketFilter.doFilter(WicketFilter.java:201)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:394)
at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:235)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:406)
at org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:185)
at org.springframework.security.util.FilterToBeanProxy.doFilter(FilterToBeanProxy.java:99)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1070)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2440)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2429)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
我使用的是符合级别 1.6 的 java 1.7。
tomcat服务器位于一台windows server 2008 R2服务器上,网站使用的技术是wicket 1.3.6。 (我知道它很古老,但我现在无能为力)。
需要注意的是,我在本地windows 7 Enterprise机器上用本地tomcat测试了这个问题,和测试服务器上的问题完全一样。
我的 Web 应用程序使用以下 Maven 依赖项捆绑在 .war 文件中:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.16</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.16</version>
</dependency>
编辑: 在 tomcat 中运行 Web 应用程序时,我注意到一个错误仅出现在 tomcat 的控制台中(而不出现在任何其他日志文件中):
Error
DOMSource cannot be processed: check that saxon8-dom.jar is on the classpath
这让我想到了 stackoverflow 上的这个问题:Apache POI error when not in IDE
但是,我没有在任何运行 tomcat 的机器上安装(也从未安装过)LibreOffice,而且无论如何,该问题的解决方案仍不清楚:是什么导致依赖项仅在 tomcat 中中断,而不是在 jetty 中中断?
最佳答案
所以最后,这就是我解决问题的方法:由于 tomcat 提示缺少 saxon librairy,我只是将它作为依赖项添加到我的 maven 项目的 pom.xml 中:
<dependency>
<groupId>net.sf.saxon</groupId>
<artifactId>saxon-dom</artifactId>
<version>8.7</version>
</dependency>
一旦我部署了这个依赖项,POI 就开始在 tomcat 中正常工作。
还不清楚的是
1) 为什么 jetty 的依赖与 tomcat 的不同。 Saxon 不在我的 CLASS_PATH 或任何明显的环境变量中。所以我的猜测是它确实存在于我的开发环境中的某处作为隐式依赖项,虽然我找不到位置,但在 tomcat 中找不到,它更独立。
2) 为什么在官方文档中根本没有提到 saxon 作为 apache POI 的依赖项。
关于java - Apache POI 在 tomcat 中崩溃但在 IDE 中不崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43788738/