问题描述
- 在阿里云ECS(Alibaba Cloud Linux 3)上,使用 Apache POI 的
SXSSFWorkbook
导出 Excel 时出现NullPointerException
。 - 服务器为无图形界面环境,JDK 为 OpenJDK 1.8(headless)。
环境信息(关键信息)
- 操作系统:阿里云ECS(Alibaba Cloud Linux 3 - 无图形界面环境)
- JDK:OpenJDK 1.8.0_412
- JDK 类型:
java-1.8.0-openjdk-headless
- Apache POI:3.14
错误堆栈(核心片段)
text
java.lang.NullPointerException
at org.apache.poi.ss.util.SheetUtil.getDefaultCharWidth(SheetUtil.java:254)
at org.apache.poi.xssf.streaming.SXSSFSheet.<init>(SXSSFSheet.java:77)
at org.apache.poi.xssf.streaming.SXSSFWorkbook.createAndRegisterSXSSFSheet(...)
问题分析
- 根因:AWT 字体系统在无图形界面环境初始化不完整,
SXSSFWorkbook
创建时需要计算默认字符宽度,触发 NPE。 - 现象触发条件:服务器无图形环境(DISPLAY 为空)、未装 X11 相关库;headless JDK 对字体配置更严格。
关键检测命令与结果(摘要)
bash
# 图形/字体/Java环境
DISPLAY:
ls -la /usr/share/fonts/ # 存在 dejavu 目录
rpm -qa | grep -i x11 # 未安装X11包
java -Djava.awt.headless=true -version
# 输出:openjdk version "1.8.0_412" ...
# JDK 信息
which java # /usr/bin/java
java -XshowSettings:properties -version | grep -E 'java.version|java.home|os.name|os.version'
# java.home=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.412... os.name=Linux
# POI 版本
find /root/chp-fbs -name '*.jar' -exec jar -tf {} \; 2>/dev/null | grep -i poi | head -5 || echo '未找到POI相关JAR
# BOOT-INF/lib/poi-ooxml-3.14.jar
# BOOT-INF/lib/poi-3.14.jar
# BOOT-INF/lib/poi-ooxml-schemas-3.14.jar
解决方案
- 添加 JVM 参数(尝试但未完全解决)
bash
java -Djava.awt.headless=true -jar your-app.jar
说明:可启动,但导出仍报 NPE,仅 headless 参数不足以补齐字体环境。
- 安装字体包与图形字体支持(最终解决)
bash
yum install -y fontconfig \
dejavu-fonts-common \
dejavu-sans-fonts \
dejavu-serif-fonts \
dejavu-sans-mono-fonts \
java-1.8.0-openjdk-devel
fc-cache -fv
安装后(节选输出):
/usr/share/fonts/dejavu: caching, new cache contents: 21 fonts
/usr/bin/fc-cache-64: succeeded
结论:安装字体 + headless 参数后,SXSSFWorkbook
对象创建正常。
- 备用方案:使用
XSSFWorkbook
替代SXSSFWorkbook
- 特点:不依赖 AWT 字体,适合中小数据量,内存开销更高(XSSFWorkbook 会全量驻内存,数据量很大时确实更占内存并可能 OOM)
- 示例(仅演示,不含项目真实代码):
java
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class TestXSSF {
public static void main(String[] args) throws Exception {
XSSFWorkbook workbook = new XSSFWorkbook();
}
}
(可选)测试 SXSSFWorkbook
的最小示例
java
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import java.io.FileOutputStream;
public class TestSXSSF {
public static void main(String[] args) throws Exception {
SXSSFWorkbook wb = new SXSSFWorkbook();
Sheet s = wb.createSheet("测试");
Row r = s.createRow(0); r.createCell(0).setCellValue("测试数据");
try (FileOutputStream out = new FileOutputStream("/tmp/test_sxssf.xlsx")) {
wb.write(out);
}
wb.close();
}
}
解决方案验证过程
- 验证字体安装
bash
fc-list | head -10
# 显示 DejaVu 系列字体(如 DejaVuSans.ttf)
fc-cache -fv
# /usr/share/fonts/dejavu: ... 21 fonts
- 验证 Java AWT 运行环境
bash
java -Djava.awt.headless=true -version
# openjdk version "1.8.0_412" ...
- 验证Excel导出接口
bash
curl -s 'http://192.168.0.100/xxx/export?page=1&rows=10' \
-H 'Accept: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' \
-o /tmp/test_export.xlsx
ls -la /tmp/test_export.xlsx
file /tmp/test_export.xlsx # Microsoft Excel 2007+
最佳实践建议
- 组合使用:安装字体包 +
-Djava.awt.headless=true
。 - 大数据量优先
SXSSFWorkbook
;若环境受限或数据量中小,可用XSSFWorkbook
。 - 控制导出规模(如分页≤1万行),必要时分段导出或改 CSV。
- 为导出任务分配足够堆内存:
-Xmx1024m
或更高,视数据量调整。 - 将环境检测与验证脚本写入部署文档,便于回归与迁移。