用Java导出POI报表需引入Apache POI依赖,通过Workbook/Sheet操作单元格写入数据,设置样式后调用Write方法输出至本地/流,支持.xls/.
以下是关于 Java 使用 Apache POI 导出报表 的详细教程,涵盖核心概念、完整代码示例、最佳实践及常见问题解决方案:
技术选型与基础准备
1 为什么选择 Apache POI?
Apache POI 是 Java 操作 Microsoft Office 文档的标准库,支持以下特性:
| 组件 | 描述 | 适用场景 |
|————|——————————-|————————|
| XSSF | .xlsx (Excel 2007+) | 新项目首选 |
| HSSF | .xls (Excel 97-2003) | 兼容旧版Office |
| HWPF | Word 文档 | 复杂排版需求 |
| HSLF | PowerPoint 幻灯片 | 演示文稿自动化 |
2 环境配置
Maven 依赖(推荐):
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version> <!-最新稳定版 -->
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
️ 注意:若需支持 .xls 格式,需额外添加 poi 依赖。
核心开发流程详解
1 创建 Excel 文件骨架
import org.apache.poi.ss.usermodel.;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.util.Date;
public class ExcelExporter {
public static void main(String[] args) throws Exception {
// 1. 创建工作簿(XSSF=xlsx, HSSF=xls)
Workbook workbook = new XSSFWorkbook(); // 默认生成xlsx格式
// 2. 创建工作表并命名
Sheet sheet = workbook.createSheet("销售统计");
// 3. 定义表头样式
CellStyle headerStyle = workbook.createCellStyle();
Font headerFont = workbook.createFont();
headerFont.setBold(true); // 加粗
headerFont.setColor(IndexedColors.BLACK.getIndex()); // 字体颜色
headerStyle.setFont(headerFont); // 应用字体
headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); // 背景色
headerStyle.setAlignment(HorizontalAlignment.CENTER); // 水平居中
// 4. 写入表头数据
Row headerRow = sheet.createRow(0); // 第1行(索引从0开始)
String[] headers = {"ID", "产品名称", "单价", "销量", "总金额", "下单日期"};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle); // 应用样式
}
// 5. 填充动态数据(模拟数据库查询结果)
Object[][] data = {
{1, "笔记本电脑", 5999.0, 120, 719880.0, new Date()},
{2, "智能手机", 2999.0, 350, 1049650.0, new Date()},
{3, "平板电脑", 1999.0, 80, 159920.0, new Date()}
};
// 6. 数据行样式配置
CellStyle dataStyle = workbook.createCellStyle();
dataStyle.setAlignment(HorizontalAlignment.CENTER);
dataStyle.setDataFormat(workbook.getCreationHelper().createDataFormat().getFormat("#,##0.00")); // 数值格式化
// 7. 遍历数据写入行
for (int rowNum = 0; rowNum < data.length; rowNum++) {
Row row = sheet.createRow(rowNum + 1); // 跳过表头行
Object[] colData = data[rowNum];
for (int colNum = 0; colNum < colData.length; colNum++) {
Cell cell = row.createCell(colNum);
if (colData[colNum] instanceof Date) {
cell.setCellValue((Date) colData[colNum]);
// 设置日期格式
CellStyle dateStyle = workbook.createCellStyle();
dateStyle.cloneStyleFrom(dataStyle);
dateStyle.setDataFormat(workbook.getCreationHelper().createDataFormat().getFormat("yyyy-MM-dd"));
cell.setCellStyle(dateStyle);
} else if (colData[colNum] instanceof Number) {
cell.setCellValue((Double) colData[colNum]);
cell.setCellStyle(dataStyle); // 应用数值格式
} else {
cell.setCellValue(colData[colNum].toString());
}
}
}
// 8. 自动调整列宽(根据内容自适应)
for (int i = 0; i < headers.length; i++) {
sheet.autoSizeColumn(i); // 逐列调整宽度
}
// 9. 冻结窗格(固定表头)
sheet.createFreezePane(0, 1); // 冻结首行+首列
// 10. 写入到文件系统
try (FileOutputStream outputStream = new FileOutputStream("销售报表_" + System.currentTimeMillis() + ".xlsx")) {
workbook.write(outputStream);
} finally {
workbook.close(); // 释放资源
}
}
}
2 关键功能解析表
| 功能 | 实现方式 | 作用 |
|---|---|---|
| 单元格样式 | CellStyle + Font + Color |
控制字体、颜色、对齐方式等 |
| 数值格式化 | DataFormat + createDataFormat() |
货币/百分比/日期等专业显示 |
| 自动换行 | cell.setWrapText(true) |
长文本自动换行 |
| 合并单元格 | sheet.addMergedRegion(new CellRangeAddress(startRow, endRow, startCol, endCol)) |
跨行/列合并 |
| 插入图片 | Drawing drawing = sheet.createDrawingPatriarch(); + Picture pict = drawing.addPicture(...) |
嵌入公司Logo等图片 |
| 添加批注 | CreationHelper helper = workbook.getCreationHelper(); Comment comment = sheet.createCellComment(cell); |
单元格注释 |
| 保护工作表 | sheet.protectSheet(password); |
限制编辑权限 |
进阶技巧与最佳实践
1 大数据量处理优化
- SXSSFEventUserModel:流式API,适合百万级数据(内存占用降低90%)
import org.apache.poi.ss.util.StreamingReader; import org.apache.poi.xssf.streaming.SXSSFWorkbook;
// 创建流式工作簿(最多保留100行内存)
SXSSFWorkbook wb = new SXSSFWorkbook(100);
Sheet sh = wb.createSheet();
// …写入数据…
wb.write(outputStream); // 直接写入输出流
wb.dispose(); // 清理临时文件
分页导出:按固定行数拆分多个Sheet,避免单个文件过大。
# 3.2 动态列宽计算
```java最大宽度调整列宽(单位:磅)
for (int i = 0; i < maxColumns; i++) {
sheet.trackColumnForAutoSizing(i); // 标记需要自动调整的列
sheet.autoSizeColumn(i, true); // true表示基于所有行的宽度计算
}
3 复杂表头设计
通过多层嵌套实现三级表头:
// 第一层表头(大分类)
Row catRow = sheet.createRow(currentRow++);
catRow.createCell(0).setCellValue("电子产品");
catRow.createCell(3).setCellValue("家居用品");
// 第二层子分类(合并单元格)
sheet.addMergedRegion(new CellRangeAddress(currentRow, currentRow, 0, 2)); // 合并A-C列
Row subCatRow = sheet.createRow(currentRow);
subCatRow.createCell(0).setCellValue("数码设备");
典型错误及解决方案
| 错误现象 | 原因分析 | 解决方案 |
|---|---|---|
| 中文显示为方框 | 缺少中文字体包 | 下载微软雅黑字体文件,通过Font类加载:font.setFontName("Microsoft YaHei") |
| 数字显示科学计数法 | 未设置数值格式 | 使用DataFormat指定格式:”#,##0.00″ |
| 文件无法打开 | 写入时未关闭流 | 确保workbook.write()后调用workbook.close() |
| 内存溢出(OOM) | 一次性加载过多数据 | 改用SXSSFWorkbook流式处理,或分批次写入 |
| 时间显示不正确 | 未设置日期格式 | 使用createDataFormat().getFormat("yyyy-MM-dd HH:mm:ss") |
相关问答FAQs
Q1: 导出的Excel文件中中文显示乱码怎么办?
A: 这是由于默认使用的字体不支持中文导致的,解决方法有两种:
- 指定中文字体:在创建
Font对象时设置字体名称为系统中存在的中文字体(如”宋体”、”微软雅黑”)。Font font = workbook.createFont(); font.setFontName("微软雅黑"); // Windows系统常用字体 font.setCharSet(FontCharSet.DEFAULT_CHARSET); // 确保字符集正确 - 嵌入字体文件:将TTF字体文件打包到项目中,通过
InputStream加载:InputStream is = new FileInputStream("simhei.ttf"); Font font = workbook.createFont(); font.setFontName("SiHei"); // 字体名称需与文件名一致 font.setFontCharSet(FontCharSet.UTF_8); // 强制使用UTF-8编码
Q2: 如何动态设置每列的不同宽度?
A: 可以通过两种方式实现:
- 精确像素控制:使用
sheet.setColumnWidth(columnIndex, widthUnits),其中widthUnits的单位是1/256个字符宽度。// 设置第2列(B列)宽度为30个字符宽度 sheet.setColumnWidth(1, 30 256); // 1代表B列(索引从0开始)
- 自动适配内容:调用
sheet.autoSizeColumn(columnIndex),该方法会根据该列中最宽的内容自动调整宽度,注意:此方法应在所有数据写入完成后调用。// 在所有数据写入完成后调用 for (int i = 0; i < maxColumns; i++) { sheet.autoSizeColumn(i); // 逐列自动调整宽度 }️ 提示:
autoSizeColumn会影响性能,建议仅
