☰
EasyExcel大数据导出与水印实现方案
2026/10/14 14:26:15 网站建设 项目流程

1. 项目背景与核心需求

最近在开发一个数据报表导出功能时,遇到了两个典型问题:一是当数据量过大时直接导出会导致内存溢出,二是业务部门要求导出的Excel文件需要带有公司水印。经过一番折腾,最终基于EasyExcel实现了稳定可靠的分批导出与水印适配方案。

这个方案特别适合需要处理以下场景的开发者:

  • 导出数据量在10万行以上的报表
  • 需要动态添加水印的Excel文件生成
  • 对服务器内存占用敏感的生产环境

2. 技术方案设计思路

2.1 为什么选择EasyExcel

相比传统的POI,EasyExcel在内存控制方面有显著优势:

  • 采用逐行写入的SAX模式解析
  • 默认缓存仅保留100行数据
  • 支持自定义分批写入策略

实测导出50万行数据时:

  • POI方式内存峰值:1.2GB
  • EasyExcel方式内存峰值:150MB

2.2 水印实现方案对比

常见的Excel水印实现方式有:

  1. 页眉页脚文字水印(兼容性好但效果差)
  2. 背景图片水印(效果佳但兼容性问题)
  3. 浮动Shape对象(折中方案)

最终选择第三种方案,因为:

  • 支持透明度和旋转角度
  • 不影响单元格数据操作
  • 在各版本Excel中显示稳定

3. 核心实现细节

3.1 分批导出实现

// 分页查询参数 @Data public class PageQuery { private int pageSize = 5000; private int currentPage = 1; } // 分批写入处理器 public class BatchWriteHandler implements WriteHandler { @Override public void sheet(int sheetNo, Sheet sheet) { // 设置批处理大小 sheet.setAutoTrim(true); } } // 导出执行方法 public void exportBigData(HttpServletResponse response) { // 1. 初始化导出参数 ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()) .registerWriteHandler(new BatchWriteHandler()) .build(); // 2. 循环分页写入 PageQuery query = new PageQuery(); while(true) { List<Data> batchData = dataService.getBatchData(query); if(CollectionUtils.isEmpty(batchData)) break; WriteSheet writeSheet = EasyExcel.writerSheet("第"+query.getCurrentPage()+"页") .head(Data.class) .build(); excelWriter.write(batchData, writeSheet); query.setCurrentPage(query.getCurrentPage()+1); } // 3. 关闭资源 excelWriter.finish(); }

关键参数说明:

  • pageSize:根据数据行宽建议设置在3000-10000之间
  • response.getOutputStream():必须提前设置好response的ContentType
  • autoTrim:设置为true可自动清理空行内存

3.2 动态水印实现

public class WatermarkHandler extends AbstractSheetWriteHandler { private final String watermarkText; public WatermarkHandler(String text) { this.watermarkText = text; } @Override public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { // 获取Sheet对象 Sheet sheet = writeSheetHolder.getSheet(); Drawing<?> drawing = sheet.createDrawingPatriarch(); // 创建文字水印 XSSFSimpleShape shape = (XSSFSimpleShape) drawing.createSimpleShape( new XSSFClientAnchor(0, 0, 0, 0, 0, 0, 10, 10)); // 水印样式设置 shape.setText(watermarkText); shape.setFillColor(0, 0, 0, 0); shape.setLineStyleColor(0, 0, 0, 0); shape.setRotation(315); // 旋转角度 shape.setFontSize(48); shape.setFontColor(200, 200, 200); // 浅灰色 } }

水印优化技巧:

  1. 透明度设置:RGB最后一个参数控制透明度(0-255)
  2. 位置计算:ClientAnchor参数控制水印位置和大小
  3. 多水印:创建多个Shape实现满屏水印效果

4. 性能优化与问题排查

4.1 内存泄漏预防

常见内存问题:

  1. 未关闭ExcelWriter导致句柄泄漏
  2. 大对象未及时GC回收
  3. 分页查询参数未重置

解决方案:

// 使用try-with-resources确保关闭 try (ExcelWriter excelWriter = EasyExcel.write(...)) { // 写入操作 } // 定期清理缓存 if(query.getCurrentPage() % 20 == 0) { System.gc(); }

4.2 水印显示异常处理

常见问题:

  1. WPS打开水印位置偏移
  2. 打印时水印消失
  3. 导出PDF格式异常

应对方案:

  1. 添加多水印交叉覆盖
  2. 同时设置页眉文字水印作为备用
  3. 测试不同办公软件的兼容性

5. 完整实现示例

结合两种功能的完整方案:

public void exportWithWatermark(HttpServletResponse response) { // 1. 响应头设置 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-Disposition", "attachment;filename=export.xlsx"); // 2. 初始化导出器 try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()) .registerWriteHandler(new WatermarkHandler("CONFIDENTIAL")) .registerWriteHandler(new BatchWriteHandler()) .build()) { // 3. 分批写入数据 PageQuery query = new PageQuery(); while(true) { List<Data> batchData = fetchBatchData(query); if(batchData.isEmpty()) break; WriteSheet writeSheet = EasyExcel.writerSheet("Page_"+query.getCurrentPage()) .head(Data.class) .build(); excelWriter.write(batchData, writeSheet); query.nextPage(); } } }

实际使用中发现几个值得注意的点:

  1. 水印文字建议控制在20个字符以内
  2. 分页大小需要根据数据列数动态调整
  3. 响应输出流需要在所有操作前设置完成

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询