一篇文章教你使用JAVA实现导入导出(EasyExcel)
1.前言
上一篇文章也已经过了很久了,讲的是java中使用poi针对Excel实现数据的灵活导入导出,我们讲讲上一
篇文章的优缺点
优点:
1、能够对excel中元素灵活使用(例如上一篇文章提到过的excel的“**名称管理器**”)
2、由于是针对业务来写的,能保持对业务的的灵活变动,可以针对当前业务的变化,修改
代码,实现对业务的贴合
3、等等......
缺点:
1、需要写的代码太多并且不公共,对于刚入门这个的人不太友好,容易看代码的时候迷失方向
2、不支持多业务导出【公共方法】万一是多业务导出,方法很容易不支持导出项目报错
方向:所以本篇文章针对上述提出来的点 我带来了这篇使用JAVA中另一个插件(EasyExcel)
实现的更加便捷的导入导出,废话不多说我们直接进入实战,接着往下看
2. 实战
2.1 导入
- EasyExcel导入详解
fileName可是是你的文件全路径名称也可以是你前端传递过来的InputStream流,然后进行数据持久化操作即可,非常简单
2.2 导出
- 引入依赖包
<!--引入EasyExcel依赖包-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>
- 编写辅助常量类
/**
* 导出Excel的常量类(这个常量类是根据我业务需求编写的,可根据所需业务做调整)
*/
public class ExcelConstant {
/**
* 默认生成得sheet名称
*/
public static final String DEFAULT_SHEET_NAME = "sheet1";
/**
* 默认列宽
*/
public static final Integer DEFAULT_COLUMN_WIDTH = 22;
/**
* 查询键 左
*/
public static final String QUERY_LEFT = "valueLeft";
/**
* 查询键 右
*/
public static final String QUERY_RIGHT = "valueRight";
/**
* 数字分隔符
*/
public static final String NUMBER_FORMAT = "#,##0.00";
/**
* 字体
*/
public static final String FONT_NAME = "宋体";
/**
* 内容文字大小
*/
public static final Short CONTENT_SIZE = 10;
/**
* 表格标题大小
*/
public static final Short MAX_HEIGHT_SIZE = 18;
/**
* 表格列大小
*/
public static final Short MIN_HEIGHT_SIZE = 10;
/**
* 查询符号
*/
public static final String QUERY_PLACEHOLDER = ":";
/**
* 表格首行高度
*/
public static final Short TABLE_TITLE_HEIGHT = 24;
}
- 编写辅助实体类
/**
* 导出Exceld的表头数据(这个常量类是根据我业务需求编写的,可根据所需业务做调整)
*/
@Data
public class ExportHeads{
/**
* 名称
*/
private String label;
/**
* 子集
*/
private List<ExportHeads> children = new ArrayList<>();
}
/**
* 导出Excel的数据实体类(这个常量类是根据我业务需求编写的,可根据所需业务做调整)
*/
@Data
@Accessors(chain = true)
public class ExportDynamic {
/**
* 文件名称
*/
private String fileName;
/**
* 文件全路径
*/
private String filePath;
/**
* 表格标题
*/
private String tableTitle;
/**
* 表格查询条件
*/
List<Map<String,String>> queryCriteria = new ArrayList<>();
/**
* 表头数据
*/
List<ExportHeads> heads =new ArrayList<>();
/**
* 表数据
*/
List<List<Object>> datas =new ArrayList<>();
}
- 公共导出工具类编写
public class BusinessExcelUtil {
/**
* @Description: 动态导出excel
*
* @Date: 2024/4/28 11:29
* @param exportDynamic 参数实体类
* @return: void
*/
public static void exportBusinessExcel(ExportDynamic exportDynamic) {
// 文件名称不能为空
if (!ObjectUtils.isEmpty(exportDynamic.getFileName())) {
List<List<String>> head = new ArrayList<>();
// 设置文件名称和文件路径
String filePath = exportDynamic.getFilePath();
String fileName = filePath + File.separator + exportDynamic.getFileName();
// 设置动态表头
if (!CollectionUtils.isEmpty(exportDynamic.getHeads())) {
// 处理查询条件以及表格的标题
head = formatHead(head(exportDynamic.getHeads()), exportDynamic.getTableTitle(), exportDynamic.getQueryCriteria());
}
// 导出excel
EasyExcel.write(fileName)
.head(head)
// .relativeHeadRowIndex(1) 表格离上方多少行
.registerWriteHandler(new SimpleColumnWidthStyleStrategy(ExcelConstant.DEFAULT_COLUMN_WIDTH))
.registerWriteHandler(setterTableHead())
.registerWriteHandler(new CustomCellWriteHandler())
.sheet(ExcelConstant.DEFAULT_SHEET_NAME)
.doWrite(exportDynamic.getDatas());
}
}
/**
* 设置表格策略
*
* @return 表格策略
*/
private static HorizontalCellStyleStrategy setterTableHead() {
// 设置样式
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
// 背景设置为颜色
headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontHeightInPoints((short) 12);
headWriteCellStyle.setWriteFont(headWriteFont);
// 内容的策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
WriteFont contentWriteFont = new WriteFont();
// 字体大小
contentWriteFont.setFontHeightInPoints((short) 12);
contentWriteCellStyle.setWriteFont(contentWriteFont);
return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
}
/**
* @Description: 处理表格标题和查询条件
*
* @Date: 2024/4/28 16:29
* @Param heads: 表头数据
* @Param title: 表头内容
* @Param queryCriteria: 查询条件
* @return: java.util.List<java.util.List < java.lang.String>>
*/
private static List<List<String>> formatHead(List<List<String>> heads, String title, List<Map<String, String>> queryCriteria) {
List<List<String>> datas = new ArrayList<>();
if (!CollectionUtils.isEmpty(heads)) {
// 取出数据长度
int size = heads.size() / 2;
String value;
for (int i = 0; i < heads.size(); i++) {
heads.get(i).add(0, title);
// 查询条件
if (!CollectionUtils.isEmpty(queryCriteria)) {
//遍历赋值查询条件
for (int j = 0; j < queryCriteria.size(); j++) {
if (i <= size) {
value = queryCriteria.get(j).get(ExcelConstant.QUERY_LEFT);
heads.get(i).add(j + 1, value);
} else {
value = ObjectUtils.isEmpty(queryCriteria.get(j).get(ExcelConstant.QUERY_RIGHT))
? queryCriteria.get(j).get(ExcelConstant.QUERY_LEFT)
: queryCriteria.get(j).get(ExcelConstant.QUERY_RIGHT);
heads.get(i).add(j + 1, value);
}
}
}
}
datas.addAll(heads);
}
return datas;
}
/**
* 处理表格表头数据
*
* @param heads 表头数据
* @return 结果
*/
private static List<List<String>> head(List<ExportHeads> heads) {
List<List<String>> list = ListUtils.newArrayList();
// 新增一级到数组
for (ExportHeads head : heads) {
List<String> datas = new ArrayList<>();
datas.add(head.getLabel());
// 判断子集
recursion(list, datas, head.getChildren(), null);
}
// 先将一级新增倒数组
return list;
}
/**
* 递归处理数据
*
* @param list 需要新增的数组
* @param heads 当前数据
*/
private static void recursion(List<List<String>> lists, List<String> list, List<ExportHeads> children, ExportHeads heads) {
if (!CollectionUtils.isEmpty(children)) {
for (ExportHeads child : children) {
List<String> listOne = new ArrayList<>();
listOne.addAll(list);
listOne.add(child.getLabel());
recursion(lists, listOne, child.getChildren(), heads);
}
} else {
lists.add(list);
}
}
}
- EasyExcel拦截器编写
/**
* EasyExcel导出拦截器
*/
@Slf4j
public class CustomCellWriteHandler extends HorizontalCellStyleStrategy implements CellWriteHandler, RowWriteHandler {
private static Integer INDEX;
@Override
public void afterRowDispose(RowWriteHandlerContext context) {
WriteSheetHolder writeSheetHolder = context.getWriteSheetHolder();
// 拿取数据判断查询条件有多少
List<String> list = writeSheetHolder.getHead().get(0);
// 拿取查询的数据的长度
INDEX = (int) list.stream().filter(item -> item.contains(ExcelConstant.QUERY_PLACEHOLDER)).count();
}
@Override
public void afterCellDispose(CellWriteHandlerContext context) {
Cell cell = context.getCell();
Row row = context.getRow();
// 这里可以对cell进行任何操作
log.info("第{}行,第{}列写入完成。", cell.getRowIndex(), cell.getColumnIndex());
if (BooleanUtils.isTrue(context.getHead())) {
setHeadCellStyle(cell, null, cell.getRowIndex());
//设置表格第一行的高度
if (cell.getRowIndex() == 0 && cell.getColumnIndex() == 0) {
row.setHeightInPoints(ExcelConstant.TABLE_TITLE_HEIGHT);
}
} else {
// 处理内容格式
setContentCellStyle(cell, null, cell.getRowIndex());
}
context.getFirstCellData().setWriteCellStyle(null);
}
@Override
protected void setHeadCellStyle(Cell headCell, Head head, Integer relativeRowIndex) {
Workbook workbook = headCell.getSheet().getWorkbook();
// 创建表头样式
CellStyle headCellStyle = workbook.createCellStyle();
Font font = workbook.createFont();
// 大标题18 小标题12
if (headCell.getRowIndex() == 0) {
font.setFontHeightInPoints(ExcelConstant.MAX_HEIGHT_SIZE); // 设置字体大小
headCellStyle.setAlignment(HorizontalAlignment.CENTER); // 水平居中
} else {
font.setFontHeightInPoints(ExcelConstant.MIN_HEIGHT_SIZE); // 设置字体大小
if (headCell.getRowIndex()>0 && headCell.getRowIndex() <= INDEX) {
// 只有查询条件列一居左 列二居右
if (headCell.getColumnIndex() == 0) {
headCellStyle.setAlignment(HorizontalAlignment.LEFT); // 居左
} else {
headCellStyle.setAlignment(HorizontalAlignment.RIGHT); // 居右
}
} else if (headCell.getRowIndex() > INDEX) {
headCellStyle.setAlignment(HorizontalAlignment.CENTER); // 居中
// 设置边框
headCellStyle.setBorderTop(BorderStyle.THIN);
headCellStyle.setBorderLeft(BorderStyle.THIN);
headCellStyle.setBorderBottom(BorderStyle.THIN);
headCellStyle.setBorderRight(BorderStyle.THIN);
}
}
font.setFontName(ExcelConstant.FONT_NAME); // 设置字体
font.setBold(false); // 加粗
headCellStyle.setFont(font);
headCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中
// 应用样式
headCell.setCellStyle(headCellStyle);
}
@Override
protected void setContentCellStyle(Cell headCell, Head head, Integer relativeRowIndex) {
Workbook workbook = headCell.getSheet().getWorkbook();
// 创建表头样式
CellStyle headCellStyle = workbook.createCellStyle();
Font font = workbook.createFont();
// 设置边框
headCellStyle.setBorderTop(BorderStyle.THIN);
headCellStyle.setBorderLeft(BorderStyle.THIN);
headCellStyle.setBorderBottom(BorderStyle.THIN);
headCellStyle.setBorderRight(BorderStyle.THIN);
DataFormat dataFormat = workbook.createDataFormat();
headCellStyle.setDataFormat(dataFormat.getFormat(ExcelConstant.NUMBER_FORMAT));
font.setFontName(ExcelConstant.FONT_NAME); // 设置字体
font.setFontHeightInPoints(ExcelConstant.CONTENT_SIZE); // 设置字体大小
headCellStyle.setFont(font);
headCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中
// 应用样式
headCell.setCellStyle(headCellStyle);
}
}
- 测试数据
List<ExportHeads> children = new ArrayList<>();
ExportHeads aa12 = new ExportHeads();
aa12.setLabel("时");
ExportHeads aa22 = new ExportHeads();
aa22.setLabel("分");
ExportHeads aa1 = new ExportHeads();
aa1.setLabel("月");
ExportHeads aa2 = new ExportHeads();
aa2.setLabel("日");
aa2.getChildren().add(aa12);
aa2.getChildren().add(aa22);
ExportHeads aa = new ExportHeads();
aa.setLabel("年");
aa.getChildren().add(aa1);
aa.getChildren().add(aa2);
ExportHeads bb = new ExportHeads();
bb.setLabel("列一");
children.add(aa);
children.add(bb);
List<List<Object>> list = ListUtils.newArrayList();
for (int i = 0; i < 10; i++) {
List<Object> data = ListUtils.newArrayList();
data.add("bb" + i);
data.add("aa" + i);
data.add(0.56);
data.add(new Date());
list.add(data);
}
List<Map<String, String>> listMam = new ArrayList<>();
Map<String, String> mam1 = new HashMap<>();
mam1.put("valueLeft", "姓名:张三");
mam1.put("valueRight", "性别:女");
listMam.add(mam1);
// 调用方法测试
BusinessExcelUtil.exportBusinessExcel(new ExportDynamic()
.setFileName("这里写你的文件名字")
.setHeads(children)
.setTableTitle("这是一个excel")
.setFilePath("这里写你的文件路径")
.setQueryCriteria(listMam)
.setDatas(list));
}
- 效果展示
- 总结
到这里我们就完成了整个导出excel公共方法的所有操作,我们总结一下这一种方法的优缺点,方便小伙伴们对应自己的业务选择不同的方法
1、相同点:都能根据自己的需求处理到每一个每一列的数据及格式
2、不同点:poi处理是写在自己的业务层,难以公共化。而EasyExcel处理是放在拦截器中 方法对应不同的需求处理不同的逻辑
3、优点:
3.1.实现了公共化处理,提供不同的数据映射出不同的Excel
3.2.方便我们针对excel的每一行甚至与每一列的数据或者样式不同处理
3.3.代码的简化,后续我们其他业务如果还需要导出excel直接传入ExportDynamic对象即可实现导出,无需再关注对于excel样式的处理或者数据的处理
4.、缺点: 如果业务需求非常的个性化,基本上就一小部分一样,大多都不一样,这样的情况就比较不推荐使用这种方式