EasyExcel实战:主标题居中与多列单元格合并技巧

张开发
2026/4/15 21:24:26 15 分钟阅读

分享文章

EasyExcel实战:主标题居中与多列单元格合并技巧
1. EasyExcel报表导出基础配置第一次用EasyExcel导出带复杂表头的报表时我被POI那套繁琐的API折磨得够呛。直到发现阿里这个神器才明白原来Java操作Excel可以这么优雅。先说说基础配置这里有个坑我踩过三次——依赖版本冲突。很多教程只让引入easyexcel核心包实际上还需要同步引入easyexcel主包dependency groupIdcom.alibaba/groupId artifactIdeasyexcel/artifactId version3.3.2/version /dependency dependency groupIdcom.alibaba/groupId artifactIdeasyexcel-core/artifactId version3.3.2/version /dependency版本号务必保持一致我有次混用了3.1.1和3.3.2结果合并单元格的功能直接罢工。建议用Maven的dependencyManagement统一管理版本避免团队协作时出现我本地是好的这种灵异事件。2. 主标题居中实现方案2.1 拦截器工作机制报表的第一行主标题要实现居中跨列合并得靠SheetWriteHandler这个神器。它就像Excel生成的监控探头能在创建sheet前后插入自定义操作。我封装了个通用拦截器你改改标题文字和列数就能直接用public class TitleMergeHandler implements SheetWriteHandler { private final String titleText; private final int mergeColumnCount; // 构造器传入标题内容和合并列数 public TitleMergeHandler(String titleText, int mergeColumnCount) { this.titleText titleText; this.mergeColumnCount mergeColumnCount; } Override public void afterSheetCreate(...) { Workbook workbook writeWorkbookHolder.getWorkbook(); Sheet sheet workbook.getSheetAt(0); // 创建标题行第0行 Row titleRow sheet.createRow(0); titleRow.setHeight((short) 500); // 行高建议设大些 // 设置标题单元格 Cell titleCell titleRow.createCell(0); titleCell.setCellValue(titleText); // 关键步骤合并单元格从第0列到mergeColumnCount-1列 sheet.addMergedRegionUnsafe(new CellRangeAddress( 0, 0, 0, mergeColumnCount - 1)); // 样式配置后面会详细展开 setTitleStyle(workbook, titleCell); } }2.2 字体样式深度定制想让标题看起来像政府红头文件这几个参数你必须掌握方正小标宋_GBK体制内最爱的公文字体20磅字号小于18显得小气大于22会换行加粗居中基本礼仪不能少private void setTitleStyle(Workbook workbook, Cell cell) { CellStyle style workbook.createCellStyle(); // 垂直水平居中 style.setVerticalAlignment(VerticalAlignment.CENTER); style.setAlignment(HorizontalAlignment.CENTER); // 字体配置 Font font workbook.createFont(); font.setBold(true); font.setFontName(方正小标宋_GBK); font.setFontHeightInPoints((short) 20); style.setFont(font); cell.setCellStyle(style); }实测发现有些Linux服务器没有方正字体会默默fallback到宋体。保险起见可以在代码里加个字体存在性检查或者提前在服务器安装字体包。3. 多级表头合并实战3.1 二维表头结构处理像财务报表那种带二级分类的表头得用**ExcelProperty注解**的数组参数。比如要生成城市/完成指标这种双层表头ExcelProperty(value { 南宁市, 当月完成指标 }, index 8) ColumnWidth(12) private BigDecimal nanNingCurMonthComplete;代码运行时EasyExcel会自动合并相同父级标题。但要注意索引值必须连续我有次把index8和index10的父级都写成南宁市结果导出后表头直接裂开。3.2 动态列宽调整技巧默认列宽经常会出现内容显示不全的尴尬推荐两种解决方案注解固定宽度适合已知列ColumnWidth(15) // 单位是字符宽度 private String projectName;代码动态计算适合动态列// 在拦截器中遍历所有列 sheet.setColumnWidth(colIndex, Math.max(headerLength, contentLength) * 256);有个隐藏技巧设置列宽时实际值是字符数*256比如要显示10个字符就填2560。这个256是Excel内部的单位换算系数官方文档可查不到。4. 生产环境避坑指南4.1 响应头设置雷区导出功能在Controller层最常见的翻车点就是响应头配置不全。必须严格按这个顺序设置response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setCharacterEncoding(UTF-8); response.setHeader(Content-Disposition, attachment;filename URLEncoder.encode(fileName, UTF-8) .replaceAll(\\, %20));我遇到过三个坑没设置ContentType导致浏览器直接显示乱码文件名包含中文时必须用URLEncoder转码IE浏览器需要特殊处理加号替换为%204.2 大数据量导出优化当数据量超过5万行时要特别注意内存溢出问题。推荐改用这种写法// 使用try-with-resources确保流关闭 try (OutputStream os response.getOutputStream()) { EasyExcel.write(os) .head(ExportVO.class) .registerWriteHandler(new TitleMergeHandler()) .sheet() .doWrite(dataList::iterator); // 使用迭代器模式 }这种写法会启用分段加载机制实测导出10万行数据内存占用能控制在200MB以内。如果数据源来自数据库建议配合MyBatis的Cursor游标使用。

更多文章