Apache POI 在 Linux 无图形界面环境下因字体配置问题导致Excel导出失败的解决方案

问题描述

  • 在阿里云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

解决方案

  1. 添加 JVM 参数(尝试但未完全解决)
bash 复制代码
java -Djava.awt.headless=true -jar your-app.jar

说明:可启动,但导出仍报 NPE,仅 headless 参数不足以补齐字体环境。

  1. 安装字体包与图形字体支持(最终解决)
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 对象创建正常。

  1. 备用方案:使用 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();
    }
}

解决方案验证过程

  1. 验证字体安装
bash 复制代码
fc-list | head -10
# 显示 DejaVu 系列字体(如 DejaVuSans.ttf)
fc-cache -fv
# /usr/share/fonts/dejavu: ... 21 fonts
  1. 验证 Java AWT 运行环境
bash 复制代码
java -Djava.awt.headless=true -version
# openjdk version "1.8.0_412" ...
  1. 验证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 或更高,视数据量调整。
  • 将环境检测与验证脚本写入部署文档,便于回归与迁移。
相关推荐
喵手3 小时前
Java中的大数据流式计算与Apache Kafka集成!
java·华为云·apache
一粒马豆3 小时前
excel表格通过前端fetch上传至后端flask处理流程示例
前端·python·flask·excel·h5·js·fetch
深思慎考8 小时前
LinuxC++项目开发日志——基于正倒排索引的boost搜索引擎(2——Parser解析html模块)
linux·c++·搜索引擎
冠希陈、9 小时前
云锁客户端连不上服务器处理
linux·运维·服务器
似水এ᭄往昔9 小时前
【Linux】--入门、基础命令
linux·服务器
做运维的阿瑞9 小时前
Linux 企业级备份体系实战:cron/anacron/restic/rclone 对比与脚本总结
linux·运维·服务器·后端·学习·系统架构·centos
喜欢吃燃面9 小时前
Linux:基本指令(一)
linux·学习
ziyue75759 小时前
idea终端添加git-bash,支持linux的shell语法
linux·git·bash·idea·软件
大聪明-PLUS9 小时前
为未知 USB 设备编写 Linux 内核驱动程序
linux·嵌入式·arm·smarc