Apache POI OPCPackage无法保存Jasper Report生成的XLSX

时间:2015-02-18 20:25:12

标签: java encryption jasper-reports apache-poi xlsx

整体行动计划是使用JasperReports API生成XLSX文件,然后将其提供给Apache POI的加密代码。在此处找到示例:http://www.quicklyjava.com/create-password-protected-excel-using-apache-poi/

不幸的是,当从Jasper Report生成文件时,我无法对文件进行加密,但是如果我要在MS Excel中创建一个文件并通过代码加载它,那么加密就好了。因此,Apache POI库可以很好地处理来自Excel的文件。

经过调试后,我相信我能够找到问题所在。

我们使用OPCPackage类打开文件(或者在另一种情况下加载输入流),该类有一个方法getParts()来设置所有字段。一个特别是packageProperties。似乎packageProperties正在加载特定的Package Part 'application / vnd.openxmlformats-package.core-properties + xml'。但是,在Jasper Report生成的XLSX文件中找不到此部分,因此保持为空。

// Check OPC compliance rule M4.1
if (part.getContentType().equals(ContentTypes.CORE_PROPERTIES_PART)) {
    if (!hasCorePropertiesPart) {hasCorePropertiesPart = true;

如果这个过去了,我们会在下面几行指定this.packageProperties:

// Core properties case-- use first CoreProperties part we come across
// and ignore any subsequent ones
if (unmarshallPart instanceof PackagePropertiesPart && needCorePropertiesPart) {
    this.packageProperties = (PackagePropertiesPart) unmarshallPart;

文件打开正常,但是当我尝试保存文件时, saveImpl 方法调用一个名为 addPackagePart 的方法传入'packageProperties'变量。然后该类抛出异常,因为该字段为null。来自 ZipPackage#saveImpl

// If the core properties part does not exist in the part list,
// we save it as well
if (this.getPartsByRelationshipType(PackageRelationshipTypes.CORE_PROPERTIES).size() == 0 && this.getPartsByRelationshipType(PackageRelationshipTypes.CORE_PROPERTIES_ECMA376).size() == 0    ) {
    logger.log(POILogger.DEBUG,"Save core properties part");
        // Add core properties to part list ...
        addPackagePart(this.packageProperties);

以下是例外:

org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException: Fail to save: an error occurs while saving the package : part
    at org.apache.poi.openxml4j.opc.ZipPackage.saveImpl(ZipPackage.java:503)
    at org.apache.poi.openxml4j.opc.OPCPackage.save(OPCPackage.java:1425)
    at com.krfs.web.controller.report.BaseReportController.encryptXlsxOutputStream(BaseReportController.java:717)
    at com.krfs.web.controller.report.BaseReportController.generateJasperReportOutput(BaseReportController.java:547)
    at com.krfs.web.controller.report.BaseReportController.processReportRunningAndGeneration(BaseReportController.java:382)
    at com.krfs.web.controller.report.StandardReportController.processReportRunningAndGeneration(StandardReportController.java:25)
    at com.krfs.web.controller.report.StandardReportController.run(StandardReportController.java:82)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:781)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:721)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:177)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at com.krfs.web.filter.RedirectFilter.doFilter(RedirectFilter.java:62)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at com.krfs.web.filter.XssFilter.doFilter(XssFilter.java:17)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.authentication.switchuser.SwitchUserFilter.doFilter(SwitchUserFilter.java:181)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:150)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at com.krfs.web.filter.CustomLoginFilter.doFilter(CustomLoginFilter.java:156)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at com.krfs.web.filter.CsrfFilter.doFilter(CsrfFilter.java:52)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at fr.xebia.servlet.filter.ExpiresFilter.doFilter(ExpiresFilter.java:1243)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at com.krfs.web.filter.SecurityHeadersFilter.doFilter(SecurityHeadersFilter.java:22)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at com.krfs.web.filter.LoggingFilter.doFilter(LoggingFilter.java:44)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.filters.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:108)
    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:170)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
    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:1040)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:315)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:744)
Caused by: java.lang.IllegalArgumentException: part
    at org.apache.poi.openxml4j.opc.OPCPackage.addPackagePart(OPCPackage.java:873)
    at org.apache.poi.openxml4j.opc.ZipPackage.saveImpl(ZipPackage.java:448)
    ... 104 more

Jasper Reports代码是导出XLSX文件的标准代码。

JRXlsAbstractExporter exporter = new JRXlsxExporter();

JasperPrint jasperPrint = fillJasperPrint(reportMessenger, reportFile, reportParameters);

exporter.setParameter(JRXlsExporterParameter.JASPER_PRINT, jasperPrint);
exporter.setParameter(JRXlsExporterParameter.OUTPUT_STREAM, outputStream);
exporter.setParameter(JRXlsExporterParameter.IS_DETECT_CELL_TYPE, Boolean.TRUE);                    
exporter.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND, Boolean.FALSE);

exporter.exportReport();

这样可以很好地创建XLSX,但是当我提取内容并查看包时,我看不到属性Part application / vnd.openxmlformats-package.core-properties + xml 显然,Apache POI需要保存文件。

加密代码:

POIFSFileSystem fs = new POIFSFileSystem();
        EncryptionInfo info = new EncryptionInfo(fs, EncryptionMode.agile);
        Encryptor enc = info.getEncryptor();
        enc.confirmPassword(reportPassword);

        try (OPCPackage opc = OPCPackage.open(new ByteArrayInputStream(xlsxOutputStream.toByteArray()))) {
            OutputStream os = enc.getDataStream(fs);
            opc.save(os);
        }

        fs.writeFilesystem(encryptedOutputStream);

依赖版本:

jasper报告 - 4.7.1(我也尝试了6.0.2,结果相同)

apache poi - 3.11

所以我的问题是,如何让这两个库协调工作,以便我可以加密来自jasper报告的文件?有没有办法在导出文件之前注入输出类型部分core-properties + xml?有没有办法围绕Apache POI库跳过找到那个部分?

我希望有人看到类似他们可以帮助我的东西。

干杯谢谢!

2 个答案:

答案 0 :(得分:1)

据我所知,Jasper Reports正在编写略微不符合规范的文件。目前还没有得到Excel不能使用它们的支持,但是不兼容它们。

Apache POI可以很好地读取这些文件,如unit tests like this one所示。 Apache POI也可以保存它们,如果通过XSSFWorkbook或类似的东西打开和保存,则触发创建缺失的部分。

不幸的是,对于您的用例,您正在OPCPacakge级别工作,并且存在POI错误。这已在r1662971中修复,并将在POI 3.12 beta 2及更高版本中。

现在,您可以通过更改以下行来触发核心属性创建:

OPCPackage opc = OPCPackage.open(inputFilePath, PackageAccess.READ_WRITE);
OutputStream os = enc.getDataStream(fs);
opc.save(os);

改为:

OPCPackage opc = OPCPackage.open(inputFilePath, PackageAccess.READ_WRITE);
OutputStream os = enc.getDataStream(fs);
opc.getProperties();
opc.save(os);

一旦您使用POI 3.12 beta 2或更高版本,您就不需要额外拨打getProperties()

此外,您链接到的github中的代码中包含一些已弃用的调用,您可能需要查看TestEncryptor.encryptPackageWithoutCoreProperties()以了解如何在不使用弃用方法的情况下稍微调整它。

答案 1 :(得分:0)

更新:stackoverflow不允许上传文件,因此您可以在github中找到整个项目:https://github.com/nestoru/xlsxenc

我可以确认这是apache POI库的错误,而不是jasper报告。我附上相关文件和临时工作涉及使用libreoffice(到目前为止最好的开源excel处理套件,实用程序甚至库)。解决方法将涉及调​​用libreoffice作为shell进程,以POI“理解”它的方式“纠正”来自jasper报告的xlsx。

以下是如何在给定提供的文件的情况下从头开始或部分重建所有内容:

  1. 从ireport加载附加的jrxml文件,该文件只输出我的徽标。
  2. 从ireport中选择xlsx输出类型,您将获得report1.xlsx。
  3. 现在尝试使用附带的java程序加密文件,您将收到如下所示的错误:

    $ javac -cp“”Enc.java $ java -cp“。:”Enc /home/dev/xlsxenc/report1.xlsx /tmp/out.xlsx

    线程“main”中的异常org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException:保存失败:保存包时发生错误:部分位于org.apache.poi.openxml4j.opc.ZipPackage.saveImpl(ZipPackage .java:503)atg.apache.poi.openxml4j.opc.OPCPackage.save(OPCPackage.java:1425)at Enc.main(Enc.java:34)引起:java.lang.IllegalArgumentException:部分在org。 apache.poi.openxml4j.opc.OPCPackage.addPackagePart(OPCPackage.java:873)org.apache.poi.openxml4j.opc.ZipPackage.saveImpl(ZipPackage.java:448)... 2更多

  4. 现在是解决方法的时候了。运行以下命令以“纠正”xlsx:

    $ libreoffice --headless --convert-to xlsx /home/dev/xlsxenc/report1.xlsx --outdir / tmp

    convert /home/dev/xlsxenc/report1.xlsx - > /tmp/report1.xlsx使用Calc Office Open XML

  5. 确认程序现在加密文件。当你打开它时会要求输入密码。只需输入“密码”即可打开。

    $ java -cp“。:*”Enc /tmp/report1.xlsx /tmp/out.xlsx

    $ libreoffice /tmp/out.xlsx

    Xlib:扩展名“XINERAMA”缺少显示“:10.0”。