概述
自上次项目完成之后,又要写下一个项目,其中需要用到「导出数据为Excel」的需求,因此学习了 EasyExcel 的技术栈。之后在对应的代码编写之中运用到了之前所学的「函数式编程」。
使用
pom
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.7</version>
</dependency>
应用
实际上如同其名,使用方法很是简单。比如我们要生成一个账单的表格。那么你可以使用「Navicat」打开你对应的表,其实它显示的是不是就是一张表格~
所以,其实生成表格只是将对应的数据转换一下而已.. 对吧! 只是要声明对应参数的列名是什么而已!
因此我们需要创建一个对应的实体类,如下。我们使用了「 @ExcelProperty」注解来声明这个参数的列名为「单元名称」。你会发现,我们还在 id 参数之上加入了「 @ExcelIgnore」注解,很显然我们不需要在表格中出现 id 这一列,所以加入此注解的参数不会渲染出来~
@ExcelIgnore
private String id;
/**
* 单元名称
*/
@ExcelProperty("单元名称")
private String unitName;
以上编写实际上不够严谨,我们最好还是要声明参数对应的索引~ 这样我们之后就可以根据索引去判断是那一列了!
/**
* 是否出售
*/
@ExcelProperty(value = "是否出售",index = 8)
private String isSale;
当然,我们还可以去声明这个列的宽度,只需要在参数之上加入「@ColumnWidth(20)」注解。也可以在实体类上面加入,这样代表每个参数多占用这个长度。这个「20」对应的是什么单位呢?这个不得而知了,文档似乎也没有说明,但「20」实际生成的列宽是「17.93字符」。不过我们不需要在意这个,只要整体协调即可。
而接下来如何使用呢,就更简单了。我们只需要声明具体的路径和文件名称,如何设置对应的实体类与数据即可。下列代码可以看出,我们创建了一个表名为「sss」的表格,并且是从「sysRoomService」的「list()」方法获取的数据,它是以「SysRoom.class」实体类为主体。
String fileName = "/Volumes/data/project/estate_management/simpleWrite" + System.currentTimeMillis() + ".xlsx";
EasyExcel.write(fileName, SysRoom.class)
.sheet("sss")
.doWrite(sysRoomService.list());
这样我们就直接生成了一个表格。
样式
通过上述我们已经能够通过数据去生成一张表格了,但是这并不够,我们有时候需要对其样式进行修改。
样式一共有两种方式修改,一种是通过渲染的拦截器去设置,一种是使用注解形式去设置。
拦截器
下放为官方文档中提供的代码,其实很好理解,我们分别设置好对应的样式,然后使用「registerWriteHandler」方法注册其中。你需要理解其中的重点——它是一个拦截器。也就是说,我们这些设置其实是告诉「HorizontalCellStyleStrategy」这个类该如何处理我们的数据样式,之后它注册进我们的任务之后会对应这些设置做出拦截修改。
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
// 背景设置为红色
headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontHeightInPoints((short)20);
headWriteCellStyle.setWriteFont(headWriteFont);
// 内容的策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
// 背景绿色
contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex());
WriteFont contentWriteFont = new WriteFont();
// 字体大小
contentWriteFont.setFontHeightInPoints((short)20);
contentWriteCellStyle.setWriteFont(contentWriteFont);
// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
HorizontalCellStyleStrategy horizontalCellStyleStrategy =
new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(fileName, DemoData.class).registerWriteHandler(horizontalCellStyleStrategy).sheet("模板")
.doWrite(data());
注解形式
第二种方式就是通过注解去设置样式,如下面代码之中,我们通过「@ContentFontStyle」注解分别设置了字体颜色和字体大小。值得注意的有两点。其一,我们的颜色值来自于「IndexedColors」枚举类中记录的颜色数值,其次如果我们不设置字体大小,它的默认大小是有问题的!
/**
* 是否出售
*/
@ContentFontStyle(color = 10,fontHeightInPoints = 11)
@ExcelProperty(value = "是否出售",index = 8)
private String isSale;
而在设置字体样式之外还有:
- @ContentStyle 设置内容样式
- @HeadFontStyle 设置头部字体样式
- @HeadStyle 设置头部样式
其中可以设置:背景颜色、字体大小、字体颜色、对齐方式等..。
需求
学完以上内容之后,我们的需求是在「是否出售」一列之中,如果内容是「已有住户」那么字体颜色就改为红色,反之为蓝色。如何实现呢?
很显然使用拦截器即可。
我们创建了一个名为「CustomCellWriteHandler」的类,并将其实现「CellWriteHandler」接口,我们在「afterCellDispose」方法中编写了以下代码。逻辑很简单只有当前不为头部以及当前索引为「8」的时候才去执行下列代码。
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
if(!isHead && cell.getColumnIndex() == 8){
Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
CellStyle cellStyle = workbook.createCellStyle();
Font font = workbook.createFont();
System.out.println(cell.getStringCellValue());
short color = cell.getStringCellValue().equals("已有住户")
? IndexedColors.RED.getIndex() : IndexedColors.BLUE.getIndex();
font.setColor(color);
cellStyle.setFont(font);
cell.setCellStyle(cellStyle);
}
}
然后我们只需要注册这个拦截器即可。
EasyExcel.write(fileName, SysRoom.class)
.sheet("sss")
.registerWriteHandler(new CustomCellWriteHandler())
.doWrite(sysRoomService.list());
但是写到这里,你应该会去思考,如果我们在生成其他表的时候能不能去复用一下这个拦截器呢?我们总不能有一个需求就重新实现一个拦截器吧?
因此我们想到了之前学习到的「函数式编程」。
函数式编程
我们创建了一个名为「CustomCellWrite」的接口。为了声明它是一个「函数式接口」我们在之上加入了「 @FunctionalInterface」注解,并且创建了一个「operation」方法。
@FunctionalInterface
public interface CustomCellWrite {
void operation(WriteSheetHolder writeSheetHolder, Cell cell, Boolean isHead);
}
其后我们在我们刚才创建的「CustomCellWriteHandler」类加入「CustomCellWrite」类型的参数,并创建对应的构造方法。
private CustomCellWrite customCellWrite;
public CustomCellWriteHandler(CustomCellWrite customCellWrite) {
this.customCellWrite = customCellWrite;
}
而在之后我们就可以将「afterCellDispose」方法直接改成下面这样。也就是说,我们创建这个拦截器类之时为其传递一个「CustomCellWrite」的实现类。很明显,看到这里应该明白了吧~ 我们在参数之中直接使用 lambda表达式即可。
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
if (customCellWrite != null) {
customCellWrite.operation(writeSheetHolder, cell, isHead);
}
}
其后,我们的代码就可以改成下面这样。
EasyExcel.write(fileName, SysRoom.class)
.sheet("sss")
.registerWriteHandler(new CustomCellWriteHandler((writeSheetHolder, cell, isHead) -> {
if (!isHead && cell.getColumnIndex() == 8) {
Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
CellStyle cellStyle = workbook.createCellStyle();
Font font = workbook.createFont();
System.out.println(cell.getStringCellValue());
short color = cell.getStringCellValue().equals("已有住户")
? IndexedColors.RED.getIndex() : IndexedColors.BLUE.getIndex();
font.setColor(color);
cellStyle.setFont(font);
cell.setCellStyle(cellStyle);
}
}))
.doWrite(sysRoomService.list());
下载接口(传递给前端)
实际上 EasyExcel 已经封装好了对应的方法,所以我们只需要把对应的数据传递一下就可以了。
/**
* 创建对应数据的表格并传递给前端
*
* @param response 响应
* @param fileName 文件名称
* @param clazz 目标数据实体类
* @param data 数据集合
* @param customCellWrite 自定义的单元格处理类
*/
private void download(HttpServletResponse response, String fileName, Class clazz, List data, CustomCellWrite customCellWrite) {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
try {
fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
ExcelWriterSheetBuilder excelWriterSheetBuilder = EasyExcel.write(response.getOutputStream(), clazz)
.sheet("sheet1");
// 自定义单元格处理器如果不为空就加入拦截器
if (customCellWrite != null) {
excelWriterSheetBuilder = excelWriterSheetBuilder.
registerWriteHandler(new CustomCellWriteHandler(customCellWrite));
}
excelWriterSheetBuilder.doWrite(data);
} catch (IOException e) {
e.printStackTrace();
}
}
本页的评论功能已关闭