You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java POI 3.17写入xlsx时出现OpenXML4JRuntimeException怎么解决?

问题复现

使用Java POI 3.17 API向新建xlsx文件写入内容时,单元测试执行正常,系统测试环节触发如下报错:

org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException: Fail to save: an error occurs while saving the package : The part /docProps/core.xml failed to be saved in the stream with marshaller org.apache.poi.openxml4j.opc.internal.marshallers.ZipPackagePropertiesMarshaller@4ae061e
at org.apache.poi.openxml4j.opc.ZipPackage.saveImpl(ZipPackage.java:595)
at org.apache.poi.openxml4j.opc.OPCPackage.save(OPCPackage.java:1539)
at org.apache.poi.POIXMLDocument.write(POIXMLDocument.java:227)
错误产生原因
  • JAXB依赖冲突:是该场景下最高发的原因。单元测试环境依赖树干净,没有额外引入JAXB相关组件,而系统测试环境大概率引入了不兼容版本的JAXB实现:比如JDK 9+自带的高版本JAXB与POI 3.17依赖的JAXB 2.2.x版本不兼容,或者项目中其他组件(如CXF、Spring OXM)引入了冲突版本的JAXB,最终导致core.xml序列化失败。
  • 临时目录权限不足:POI写入xlsx时会在系统临时目录生成临时文件,单元测试用本地用户权限执行,对临时目录有读写权限;但系统测试环境的运行账号(如Linux下的服务运行账号、容器内的非root账号)没有java.io.tmpdir对应目录的读写权限,无法完成临时文件的序列化写入。
  • 输出流提前关闭/被占用:系统测试的代码链路比单元测试长,可能存在过滤器、拦截器或者其他业务逻辑,在POI执行write操作前就关闭了目标输出流,或者输出流已经被写入了无关数据导致格式损坏。
  • 文档属性包含非法字符:如果代码中设置了xlsx的文档属性(作者、标题、备注等),单元测试用的测试值都是合法字符,而系统测试时传入了XML非法字符(如\0、\u0001等控制字符),会导致core.xml序列化报错。
修复方案
  • 解决JAXB依赖冲突
    1. 在项目依赖中显式锁定POI 3.17配套的JAXB版本,排除其他依赖引入的高版本JAXB,Maven参考配置如下:
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.17</version>
</dependency>
<!-- 锁定JAXB版本适配POI 3.17 -->
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.2.12</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-impl</artifactId>
    <version>2.2.12</version>
</dependency>
  1. 如果运行环境是JDK 9+,需要额外添加JVM启动参数--add-modules java.xml.bind,或者直接把JAXB相关依赖打包进应用,避免使用JDK自带的高版本JAXB。
  • 解决临时目录权限问题:检查系统测试环境运行账号对java.io.tmpdir指定目录的读写权限,也可以在启动参数中指定有权限的临时目录:-Djava.io.tmpdir=/path/to/writable/dir
  • 解决输出流异常:排查系统测试链路中输出流的操作逻辑,确保POI的write方法执行完成前输出流不会被关闭,且没有其他逻辑向同一个输出流写入无关数据。
  • 解决非法字符问题:对设置的文档属性值做XML非法字符过滤,移除所有ASCII控制字符(除换行、制表、回车),参考实现代码:
public static String filterXmlInvalidChar(String input) {
    if (input == null) return "";
    return input.replaceAll("[^\\u0009\\u000a\\u000d\\u0020-\\uD7FF\\uE000-\\uFFFD]", "");
}

内容的提问来源于stack exchange,提问作者Jen_H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 02:48:04