Hutool操作excel相较于easyexcel更加的简单灵活,适合数据量小、格式不太复杂的excel操作
此excel导出格式较为复杂,较多的合并单元格。首先定义具体类,采用list集合封装,调用合并方法进行合并单元格即可。
导入依赖
cn.hutool
hutool-all
5.7.19
org.apache.poi
poi-ooxml
4.0.0
//导出excel
// 通过工具类创建writer
ExcelWriter writer = ExcelUtil.getWriter();
int number = HutoolExcelUtil.setHeaderAlias(writer, DataTbbTableVO.class);
// 默认的,未添加alias的属性也会写出,如果想只写出加了别名的字段,可以调用此方法排除之
writer.setOnlyAlias(true);
//标题字体样式
Font font = HutoolExcelUtil.createFont(writer, true, false, "仿宋_GB2312", 22);
//合并单元格后的标题行,使用默认标题样式
CellStyle cellStyle = HutoolExcelUtil.createCellStyle(writer, font, true, VerticalAlignment.CENTER, HorizontalAlignment.CENTER);
writer.merge(0, 0, 0, number - 1, dataTbbInfo.getTitle(), cellStyle);
//跳过当前行 这里我也不知道为什么要调用,不调用内容表头不会显示
writer.passCurrentRow();
//写入标题
writer.writeHeadRow(Arrays.asList(""));
//全局字体默认样式
Font font1 = HutoolExcelUtil.createFont(writer, false, false, "仿宋_GB2312", 12);
//设置全局样式
StyleSet styleSet = HutoolExcelUtil.setBaseGlobalStyle(writer, font1);
//设置标题样式
HutoolExcelUtil.createHeadCellStyle(styleSet, font1, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
//手动设置列宽
HutoolExcelUtil.setSizeColumn(writer, Arrays.asList(5, 12, 16, 15, 14, 15, 30, 15, 15, 12), number - 1);
//合并单元格
Map merge = this.merge(dataTbbInfo.getDataTbbTableVOS(), writer);
//设置筛选下拉框
HutoolExcelUtil.setFilter(writer, "A2:D" + merge.get("index"));
//一次性写出内容,使用默认样式,强制输出标题
writer.write((List>) merge.get("result"), true);
//下载文件
HutoolExcelUtil.downloadExcel(response, dataTbbInfo.getTitle(), writer);
/**
* @author z
* @Description 导出excel工具类
* @date 2022/11/21 18:21
*/
public class HutoolExcelUtil {
/**
* 方法描述: 全局基础样式设置
* 默认 全局水平居中+垂直居中
* 默认 自动换行
* 默认单元格边框颜色为黑色,细线条
* 默认背景颜色为白色
*
* @param writer writer
* @param font 字体样式
* @return cn.hutool.poi.excel.StyleSet
*/
public static StyleSet setBaseGlobalStyle(ExcelWriter writer, Font font) {
//全局样式设置
StyleSet styleSet = writer.getStyleSet();
//设置全局文本居中
styleSet.setAlign(HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
//设置全局字体样式
styleSet.setFont(font, true);
//设置背景颜色 第二个参数表示是否将样式应用到头部
styleSet.setBackgroundColor(IndexedColors.WHITE, true);
//设置自动换行 当文本长于单元格宽度是否换行
styleSet.setWrapText();
// 设置全局边框样式
styleSet.setBorder(BorderStyle.THIN, IndexedColors.BLACK);
return styleSet;
}
/**
* 方法描述: 设置标题的基础样式
*
* @param styleSet StyleSet
* @param font 字体样式
* @param horizontalAlignment 水平排列方式
* @param verticalAlignment 垂直排列方式
* @return org.apache.poi.ss.usermodel.CellStyle
*/
public static CellStyle createHeadCellStyle(StyleSet styleSet, Font font,
HorizontalAlignment horizontalAlignment,
VerticalAlignment verticalAlignment) {
CellStyle headCellStyle = styleSet.getHeadCellStyle();
headCellStyle.setAlignment(horizontalAlignment);
headCellStyle.setVerticalAlignment(verticalAlignment);
headCellStyle.setFont(font);
return headCellStyle;
}
/**
* 方法描述: 设置基础字体样式字体 这里保留最基础的样式使用
*
* @param bold 是否粗体
* @param fontName 字体名称
* @param fontSize 字体大小
* @return org.apache.poi.ss.usermodel.Font
*/
public static Font createFont(ExcelWriter writer, boolean bold, boolean italic, String fontName, int fontSize) {
Font font = writer.getWorkbook().createFont();
//设置字体名称
font.setFontName(fontName);
//设置是否斜体
font.setItalic(italic);
//设置字体大小 以磅为单位
font.setFontHeightInPoints((short) fontSize);
//设置是否加粗
font.setBold(bold);
return font;
}
/**
* 方法描述: 设置行或单元格基本样式
*
* @param writer writer
* @param font 字体样式
* @param verticalAlignment 垂直居中
* @param horizontalAlignment 水平居中
* @return void
*/
public static CellStyle createCellStyle(ExcelWriter writer, Font font, boolean wrapText,
VerticalAlignment verticalAlignment,
HorizontalAlignment horizontalAlignment) {
CellStyle cellStyle = writer.getWorkbook().createCellStyle();
cellStyle.setVerticalAlignment(verticalAlignment);
cellStyle.setAlignment(horizontalAlignment);
cellStyle.setWrapText(wrapText);
cellStyle.setFont(font);
return cellStyle;
}
/**
* 方法描述: 设置边框样式
*
* @param cellStyle 样式对象
* @param bottom 下边框
* @param left 左边框
* @param right 右边框
* @param top 上边框
* @return void
*/
public static void setBorderStyle(CellStyle cellStyle, BorderStyle bottom, BorderStyle left, BorderStyle right,
BorderStyle top) {
cellStyle.setBorderBottom(bottom);
cellStyle.setBorderLeft(left);
cellStyle.setBorderRight(right);
cellStyle.setBorderTop(top);
}
/**
* 方法描述: 获取对象需要导出的列和别名 这里按字段顺序来(可以在自定义注解上添加属性标识字段顺序,重写方法)
* 在需要导出的字段上贴上注解 这里是@ExcelProperty,也可以用自定义注解
* 注解需要有value 标识别名 order 标识字段顺序
*
* @param clazz 对象类型
* @return int 导出的字段个数
*/
public static int setHeaderAlias(ExcelWriter writer, ExcelReader reader, Class> clazz) {
//需要导出的字段数
TreeMap> headerAliasMap = new TreeMap<>();
//判断类中字段类型和是否有别名注释
HutoolExcelUtil.getResult(headerAliasMap, clazz);
LinkedHashMap linkedMap = new LinkedHashMap<>();
for (Map.Entry> entry : headerAliasMap.entrySet()) {
linkedMap.putAll(entry.getValue());
}
Optional.ofNullable(writer).ifPresent(w -> w.setHeaderAlias(linkedMap));
Optional.ofNullable(reader).ifPresent(r -> r.setHeaderAlias(linkedMap));
return linkedMap.size();
}
/**
* 判断类中字段类型和是否有别名注释
*
* @param headerAliasMap map集合
* @param clazz 类
*/
private static void getResult(TreeMap> headerAliasMap, Class> clazz) {
Field[] fields = clazz.getDeclaredFields();
Arrays.stream(fields).forEach(f -> {
//获取字段类型
Class> type = f.getType();
//判断字段类型是否是list
if (type.equals(List.class)) {
//获取集合的泛型对象
Type genericType = f.getGenericType();
if (genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
Class> aClass = (Class>) pt.getActualTypeArguments()[0];
//递归调用
HutoolExcelUtil.getResult(headerAliasMap, aClass);
}
}
//判断是否有注解
if (f.isAnnotationPresent(ExcelProperty.class)) {
ExcelProperty annotation = f.getAnnotation(ExcelProperty.class);
//别名
String[] value = annotation.value();
int order = annotation.order();
Map alisMap = new HashMap<>();
String fieldName = f.getName();
//字段名
String headerAlias = value[0];
alisMap.put(fieldName, headerAlias);
headerAliasMap.put(order, alisMap);
}
});
}
/**
* 方法描述: 自适应宽度(中文支持)
*
* @param sheet 页
* @param size 因为for循环从0开始,size值为 列数-1
* @return void
*/
public static void setSizeAutoColumn(Sheet sheet, int size) {
for (int columnNum = 0; columnNum <= size; columnNum++) {
int columnWidth = sheet.getColumnWidth(columnNum) / 256;
for (int rowNum = 0; rowNum <= sheet.getLastRowNum(); rowNum++) {
Row currentRow;
//当前行未被使用过
if (sheet.getRow(rowNum) == null) {
currentRow = sheet.createRow(rowNum);
} else {
currentRow = sheet.getRow(rowNum);
}
if (currentRow.getCell(columnNum) != null) {
Cell currentCell = currentRow.getCell(columnNum);
if (currentCell.getCellType() == CellType.STRING) {
int length = currentCell.getStringCellValue().getBytes().length;
if (columnWidth < length) {
columnWidth = length;
}
}
}
}
sheet.setColumnWidth(columnNum, columnWidth * 256);
}
}
/**
* 方法描述: 手动宽度(中文支持)
*/
public static void setSizeColumn(ExcelWriter excelWriter, List list, int size) {
for (int columnNum = 0; columnNum <= size; columnNum++) {
excelWriter.setColumnWidth(columnNum, list.get(columnNum));
}
}
/**
* 方法描述: excel 导出下载
*
* @param response 响应
* @param fileName 文件名
* @param writer writer
* @return void
*/
public static void downloadExcel(HttpServletResponse response, String fileName, ExcelWriter writer) {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
// xxx.xlsx是弹出下载对话框的文件名
ServletOutputStream out = null;
try {
// 设置请求头属性
fileName = URLEncoder.encode(fileName, "UTF-8");
response.setHeader("Content-Disposition",
"attachment;filename=" + new String((fileName + ".xlsx").getBytes(), StandardCharsets.ISO_8859_1));
out = response.getOutputStream();
// 写出到文件
writer.flush(out, true);
// 关闭writer,释放内存
writer.close();
// 此处记得关闭输出Servlet流
IoUtil.close(out);
} catch (IOException e) {
throw new BusinessException("导出失败");
}
}
/**
* 方法描述: 最简单的导出,直接将数据放到excel
* 导入的数据最好是List
/**
合并单元格方法
*/
private Map merge(List dataTbbTableVOS, ExcelWriter writer) {
Map map = new HashMap<>(2);
//有个字段要合并的就定义多少
int index1 = 2;
int index2 = 2;
// 后面要用到的临时对象
List> rows = new LinkedList<>();
List