以下是关于 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
会影响性能,建议仅
原创文章,发布者:酷盾叔,转转请注明出处:https://www.kd.cn/ask/96384.html