案例
Excel
导入导出,大部分使用 easy-poi
或者 easy-excel
两个工具类就可以了,对于简单的一行一行(像关系型数据库表记录)的导出那可真的是啪的一声,很快啊。
就像下图这样:
可是要想导出成下图:
这就有点难度了。
工具现成方法
easy-poi
easy-poi
注解导出,@Excel
注解有个 needMerge
属性,属性描述:是否需要纵向合并单元格(用于含有list中,单个的单元格,合并list创建的多个row),这个属性默认为false
,若为true
,则会将当前单元格与上一行同列单元格进行比较,如果一样,则会合并单元格,这样应付上图是可以的,但是要是跨列,好像就不支持了。
easy-excel
easy-excel
注解导出有个 @ContentLoopMerge
注解,表示每隔几列合并单元格,这样对于有固定规律的表格是没问题的,如果是 23333 这种,应该也就不支持了。
利用 poi Api
完成
思路
- 首先根据渲染的数据,根据需要合并的属性,进行分组,然后计算出,合并单元格数量
- 利用
easy-poi
和easy-excel
渲染Excel
,获得Workbook
- 再根据导出
sheet
名称,获取Sheet
- 利用
Sheet#addMergedRegion
方法进行合并 - done
计算获得合并单元格
一般渲染都是list
,可以利用 stream 操作进行 GroupBy
TreeMap<String, List<E>> treeMap = list.stream()
.collect(Collectors.groupingBy(key,
() -> new TreeMap<String, List<E>>(comparator),
Collectors.toList()));
List<Integer> groups = Lists.newArrayList();
for (Map.Entry<String, List<E>> entry : treeMap.entrySet()) {
groups.add(entry.getValue().size());
}
这里主要讲下,一定要用TreeMap
这个结构,因为HashMap
这种key
是乱序的,一旦乱序,就会与已经导出的Excel
不一致,一旦不一致,就是乱合并。
所以groupingBy
的第二个参数里面的比较器就尤为重要了,一定要用比较器去控制排序与真实Excel
一致。
分组完毕之后就是将,每个分组size
记录下来,以便进行计算格子。
/**
* 计算合并格子
*
* @param startRow 表头最后行
* @param cols 需要合并的列 数组
* @return
*/
public List<CellRangeAddress> computeRange(int startRow, int[] cols) {
List<CellRangeAddress> mergeCell = Lists.newArrayList();
for (Integer group : this.groups) {
int interval = group;
if (interval == 1) {
startRow = startRow + 1;
continue;
}
int beginRow = startRow;
int endRow = beginRow + interval - 1;
for (int col : cols) {
CellRangeAddress range = singleRowCellRange(beginRow, endRow, col);
mergeCell.add(range);
}
startRow = endRow + 1;
}
return mergeCell;
}
每次只能计算一个合并列,需要过滤表头以及知道合并的列,计算获得一系列的CellRangeAddress
合并
Sheet sheet = workbook.getSheet(sheetName);
List<CellRangeAddress> addresses = ranges.get(sheetName);
for (CellRangeAddress cellAddresses : addresses) {
sheet.addMergedRegion(cellAddresses);
}
END
按照这个方法,基本上可以实现任意合并了,只是稍微有些繁琐。我自己整理了个工具方法,可以稍微简化下流程。
SheetMerge<TestEntity> sheetMerge = new SheetMerge(list);
sheetMerge.range("test", TestEntity::getA, (a, b) -> CompareUtil.compare(a, b), new int[]{0}, 1);
ExportParams exportParams = new ExportParams();
exportParams.setSheetName("test");
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, TestEntity.class, list);
sheetMerge.merge(workbook);
@Cleanup
BufferedOutputStream outputStream = FileUtil.getOutputStream(file_name);
workbook.write(outputStream);
最后贴下源码地址:excel-merge