Hutool:https://www.hutool.cn/
参考文档:https://hutool.cn/docs/#/
API文档:https://apidoc.gitee.com/loolly/hutool/
Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。
Hutool是项目中“util”包友好的替代,它节省了开发人员对项目中公用类和公用工具方法的封装时间,使开发专注于业务,同时可以最大限度的避免封装不完善带来的bug。
今天要讲的是excel的导出并合并单元格,其他工具类,可查看参考文档,之后也会陆续的更新一些常用工具类的用法。
之前常用的是Apache的POI库,代码复杂、非常耗内存,严重时会导致内存溢出,并发上来,一定会OOM或频繁full gc,后来发现平时使用的Hutool工具类,也有excel的导入导出API,使用起来简单方便。Hutool-poi是针对Apache POI的封装,因此需要用户自行引入POI库,Hutool默认不引入
引入依赖:
推荐引入poi-ooxml,这个包会自动关联引入poi包,且可以很好的支持Office2007+的文档格式
普通导出
/**
* @Author zmyhow
* @Description 普通导出excel
* @Date 11:01 2020/12/16
* @param response 响应
* @param fileName excel文件名
* @param sheetName sheet名称
* @param headers 表头别名map,有序maplinkHashMap,才能保证表头是有序的
* @param data 数据集合
* @return void
*/
public void exportExcelByHutools(HttpServletResponse response,
String fileName,
String sheetName,
Map<String, String> headers,
List<T> data) {
//通过工具类创建writer
try {
//对于大量数据输出,采用bigwriter,其他方法不变
// ExcelWriter writer = ExcelUtil.getBigWriter();
ExcelWriter writer = ExcelUtil.getWriter();
//设置sheet的名称
writer.renameSheet(sheetName);
//设置head的名称, 此时的顺序就是导出的顺序, key就是属性名称, value就是别名
headers.entrySet().forEach(entry -> {
//这个添加顺序和导出顺序相同
writer.addHeaderAlias(entry.getKey(), entry.getValue());
});
writer.write(data, true);
response.reset();
response.setContentType("application/vnd.ms-excel;charset=utf-8");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
writer.flush(response.getOutputStream());
writer.close();
} catch (Exception e) {
//如果导出异常,则生成一个空的文件
log.info("######导出 excel异常 :{}", e.getMessage());
}
}
其他导出方法可参考GitHub:https://github.com/looly/hutool/blob/v5-master/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelWriteTest.java
今天重点是合并单元格,最近遇到导出excel需要自定义字段合并单元格,网上查了一些,资料比较少,有些是针对特定项目的逻辑处理,没有一个通用的方法,于是自己就着手写了一个
支持自定义表头别名,合并单元格,创建writer的方法可根据业务需要进行修改。
/**
* @Author zmyhow
* @Description hutool导出excel
* @Date 10:28 2020/12/16
* @param dataList 数据集合
* @param fileName excel文件名
* @param headAliasMap 表头别名
* @param mergeNameColumnIndexMap 合并单元格的字段名和列角标,角标从0开始
* @param isMerge 是否需要合并单元格
* @return void
*/
public static void hutoolExportExcel(List<?> dataList,
String fileName,
LinkedHashMap<String, String> headAliasMap,
LinkedHashMap<String, Integer> mergeNameColumnIndexMap,
Boolean isMerge) throws Exception {
// 通过工具类创建writer
ExcelWriter writer = ExcelUtil.getWriter(fileName);
//自定义标题别名
if (MapUtil.isNotEmpty(headAliasMap)) {
writer.setHeaderAlias(headAliasMap);
}
if (isMerge) {
//获取需要合并的单元格所对应的行集合
Map<Integer, List<RowRangeDto>> stringListMap = addMerStrategy(dataList, mergeNameColumnIndexMap);
//调用merge合并单元格
layout(writer, stringListMap);
}
// 一次性写出内容,使用默认样式,强制输出标题
writer.write(dataList, true);
// flush或者close方法后才会真正写出文件;关闭writer,才会释放Workbook对象资源
writer.close();
}
addMerStrategy:获取合并单元格对应的行集合map
/**
* @Author zmyhow
* @Description 获取合并单元格对应的行集合
* @Date 11:16 2020/12/16
* @param dataList 数据集合
* @param mergeNameColumnIndexMap 有序map,合并单元格字段和列角标map
* @return 合并单元格对应的行集合映射
*/
public static Map<Integer, List<RowRangeDto>> addMerStrategy(List<?> dataList, LinkedHashMap<String, Integer> mergeNameColumnIndexMap) throws Exception {
Map<Integer, List<RowRangeDto>> strategyMap = new HashMap<>();
Object preObj = null;
int i = 0;
for (Object currObj : dataList) {
if (preObj != null) {
Boolean mergeFlag = false;
int j = 0;
for (Map.Entry<String, Integer> entry : mergeNameColumnIndexMap.entrySet()) {
//在第一个列合并的情况下,后面的列才需要合并
if (mergeFlag || j == 0) {
String name = entry.getKey();
if (getGetMethod(currObj, name).equals(getGetMethod(preObj, name))) {
fillStrategyMap(strategyMap, mergeNameColumnIndexMap.get(name), i);
mergeFlag = true;
}
} else {
break;
}
j++;
}
}
i++;
preObj = currObj;
}
return strategyMap;
}