Java生成Excel案例

wen java案例 2

本文目录导读:

Java生成Excel案例

  1. 目录导读
  2. 为什么Java开发者需要掌握Excel生成?
  3. 主流方案对比:Apache POI vs Alibaba EasyExcel vs JXL
  4. 实战案例一:基于POI生成带样式和公式的Excel
  5. 实战案例二:百万级数据用EasyExcel流式写入
  6. 常见性能陷阱与内存优化策略
  7. FAQ:高频问题深度解析
  8. 总结与选型建议

Java生成Excel终极指南:从POI到EasyExcel的实战案例与性能对比

目录导读

  1. 为什么Java开发者需要掌握Excel生成?
  2. 主流方案对比:Apache POI vs Alibaba EasyExcel vs JXL
  3. 实战案例一:基于POI生成带样式和公式的Excel
  4. 实战案例二:百万级数据用EasyExcel流式写入
  5. 常见性能陷阱与内存优化策略
  6. FAQ:高频问题深度解析
  7. 总结与选型建议

为什么Java开发者需要掌握Excel生成?

在企业级应用中,报表导出、数据备份、批量导入模板等场景几乎离不开Excel,Java作为后端主流语言,生成Excel的能力直接决定了系统的交付质量,搜索引擎上关于“Java生成Excel案例”的诉求量常年位居技术榜单前列,而多数教程只停留在“能跑”阶段,缺乏对性能与内存的深度剖析,本文结合Apache POI与EasyExcel两大生态,通过可复现的代码案例,帮你避开常见的坑。

主流方案对比:Apache POI vs Alibaba EasyExcel vs JXL

方案 内存占用 学习曲线 功能丰富度 适用场景
Apache POI (XSSF) 高(DOM模型) 复杂样式、公式、图表
POI + SXSSF 中(流式写入) 大文件写入
Alibaba EasyExcel 低(SAX模式) 大数据量读写
JXL (已停更) 遗产项目维护

关键结论:若需导出超过5万行数据,POI的XSSFWorkbook会引发OOM,必须改用SXSSF或EasyExcel。

实战案例一:基于POI生成带样式和公式的Excel

// 引入依赖:org.apache.poi:poi-ooxml:5.2.3
public static void createStyledExcel(String path) throws IOException {
    try (XSSFWorkbook workbook = new XSSFWorkbook()) {
        XSSFSheet sheet = workbook.createSheet("销售报表");
        // 创建标题行样式
        XSSFCellStyle headerStyle = workbook.createCellStyle();
        headerStyle.setFillForegroundColor(IndexedColors.DARK_BLUE.getIndex());
        headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        headerStyle.setFont(createBoldFont(workbook));
        // 创建数据行
        String[] headers = {"月份", "销售额", "成本", "利润"};
        Row row0 = sheet.createRow(0);
        for (int i = 0; i < headers.length; i++) {
            Cell cell = row0.createCell(i);
            cell.setCellValue(headers[i]);
            cell.setCellStyle(headerStyle);
        }
        // 填充数据(示例3行)
        Object[][] data = {
                {"一月", 12000, 8000, null},
                {"二月", 15000, 9000, null},
                {"三月", 18000, 11000, null}
        };
        for (int i = 0; i < data.length; i++) {
            Row row = sheet.createRow(i + 1);
            row.createCell(0).setCellValue((String) data[i][0]);
            row.createCell(1).setCellValue((Integer) data[i][1]);
            row.createCell(2).setCellValue((Integer) data[i][2]);
            // 设置公式:利润 = 销售额 - 成本
            Cell formulaCell = row.createCell(3);
            formulaCell.setCellFormula(String.format("B%d-C%d", i + 2, i + 2));
        }
        // 自动调整列宽
        sheet.autoSizeColumn(0);
        sheet.autoSizeColumn(1);
        try (FileOutputStream fos = new FileOutputStream(path)) {
            workbook.write(fos);
        }
    }
}

注意autoSizeColumn在数据量大时非常消耗性能,建议对固定列宽手动设置sheet.setColumnWidth(index, width * 256)

实战案例二:百万级数据用EasyExcel流式写入

对于超过10万行的场景,EasyExcel采用SAX解析模式,内存占用仅几十KB,以下为最佳实践:

// 依赖:com.alibaba:easyexcel:3.3.2
@Data
public class OrderData {
    @ExcelProperty("订单号")
    private String orderId;
    @ExcelProperty("金额")
    private BigDecimal amount;
    @ExcelProperty("状态")
    private String status;
}
public static void massiveWrite(String path) {
    // 先写表头
    EasyExcel.write(path, OrderData.class)
            .sheet("订单")
            .doWrite(() -> {
                // 分页查询数据库,每次返回1万条
                for (int page = 0; page < 100; page++) {
                    List<OrderData> batch = queryFromDB(page, 10000);
                    if (batch.isEmpty()) {
                        break;
                    }
                    return batch; // EasyExcel会自动分批次写入
                }
                return Collections.emptyList();
            });
}

核心原理:EasyExcel将数据逐行转换为字符串后直接写入临时文件,最后再合并,避免整个工作簿驻留内存。

常见性能陷阱与内存优化策略

  • 陷阱1:使用XSSFWorkbook加载模板再填充 —— 模板文件若超过5MB,建议改用OPCPackage.open(InputStream)读取并转为SXSSFWorkbook
  • 陷阱2:频繁创建Fontstyle对象 —— 每个单元格如果单独new样式,会产生大量Java对象,正确做法是复用预定义的样式常量。
  • 陷阱3:忘记设置SXSSFWorkbook的滑动窗口大小 —— 默认100行,如果单个Sheet超过5万行,建议设置为500,并在写完后调用dispose()释放临时文件。
  • 优化建议:使用@ExcelIgnore注解忽略不需要的字段,用EasyExcel.write().autoCloseStream(false)配合ByteArrayOutputStream做网络传输。

FAQ:高频问题深度解析

Q1:POI生成Excel后乱码怎么办?

A:检查单元格类型是否设置正确,如果是中文,用cell.setCellValue(String),同时确保响应头设置Content-Dispositionattachment; filename*=UTF-8''${encodeURIComponent(fileName)}

Q2:如何导出多Sheet的工作簿?

A:EasyExcel支持excelWriter.write(List, sheetName)多次调用,但务必每次传入不同的Sheet名称,POI则直接创建多个XSSFSheet

Q3:生成Excel后如何转化为PDF打印?

A:推荐先用POI生成Excel,再用LibreOffice无头模式转换(soffice --headless --convert-to pdf),注意处理中文字体配置。

Q4:大数据量导出时如何避免OOM?

A:终极方案是走异步任务(如Spring @Async),返回下载链接,前端轮询完成后展示,同时配合SXSSFWorkbook(windowSize=500, compressTempFiles=true)

Q5:EasyExcel支持模板填充吗(类似POI的fill)?

A:支持,使用.withTemplate(fillTemplateFile()),但模板中必须用{变量名}占位,注意填充复杂列表时,需要在模板第二行开始放数据。

总结与选型建议

需求维度 推荐方案
<1万行,样式复杂(合并单元格、图表) Apache POI (XSSF)
1万~10万行,列较多 POI + SXSSF
>10万行,列结构固定 EasyExcel
需要读Excel做导入 EasyExcel(内存安全)

最后提醒:无论选择哪种方案,务必在生产环境压测内存,本文的案例代码均可直接运行,建议你结合搜索引擎上真实业务错误报告(如“Java生成Excel内存溢出”),二次深挖底层源码,才能写出真正健壮的导出服务。


如果你在项目中遇到过其他奇葩问题,欢迎在评论区留言,我会挑选高频问题继续深挖。

抱歉,评论功能暂时关闭!