easy-excel通用异步导入导出神辅助
admin
2024-05-22 01:55:18

async-excel是easy-excel的辅助组件,抽取通用异步逻辑,通过引入一个starter配置个数据源就可以让导入导出变成异步,无需额外的任何代码,不改变easy-excel的任何特性。

为了支持业务上日益变态的需求,对async-excel进行了一轮重构
当前版本1.1.0。修改了部分写法,兼容1.0.0版本。一些方法被标注为过时。将会在以后的某个版本中移除。

1.1.0版本重构主要内容如下

  • 导出的sheet的声明从框架内部移动到了handler中由开发者自己声明,让easy-excel的功能能够灵活化。
  • 添加了回调的支持,开发者可能会根据整个导入结果需要做一些回调处理
  • 导入的动态表头支持

gitee地址

github地址

demo地址

如果项目对你有帮助可以给个star支持下

async-excel基于easy-excel抽取了异步逻辑,并且使用了sping的父子容器,适配了springboot-starter,使用该组件非常简单

引入starter

com.asyncexcelasync-excel-springboot-starter1.1.0

初始化数据库

drop table if exists excel_task;
CREATE TABLE `excel_task` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',`type` tinyint(2) NOT NULL COMMENT '类型:1-导入,2-导出',`status` tinyint(2) NOT NULL DEFAULT 0 COMMENT '状态:0-初始,1-进行中,2-完成,3-失败',`estimate_count` bigint(20) NOT NULL DEFAULT 0 COMMENT '预估总记录数',`total_count` bigint(20) NOT NULL DEFAULT 0 COMMENT '实际总记录数',`success_count` bigint(20) NOT NULL DEFAULT 0 COMMENT '成功记录数',`failed_count` bigint(20) NOT NULL DEFAULT 0 COMMENT '失败记录数',`file_name` varchar(200) DEFAULT NULL COMMENT '文件名',`file_url` varchar(500) DEFAULT NULL COMMENT '文件路径',`failed_file_url` varchar(500) DEFAULT NULL COMMENT '失败文件路径',`failed_message` varchar(255) DEFAULT NULL COMMENT '失败消息',`start_time` datetime DEFAULT NULL COMMENT '开始时间',`end_time` datetime DEFAULT NULL COMMENT '结束时间',`tenant_code` varchar(50) default NULL COMMENT '租户编码',`create_user_code` varchar(50) default NULL COMMENT '用户编码',`business_code` varchar(50) default NULL COMMENT '业务编码',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='导入导出任务';

配置数据源,父子容器多数据源配置,不影响你原有数据源

#aysncexcel 数据源
spring.excel.datasource.url=jdbc:mysql://localhost:3306/async-excel?serverTimezone=GMT%2B8&autoReconnect=true&allowMultiQueries=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&&useCursorFetch=true&&rewriteBatchedStatements=true
spring.excel.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.excel.datasource.password=root
spring.excel.datasource.username=root

多sheet导出
controller 注入excelService

@AutoWire
ExcelService  excelService
    //导出最简示例,支持多个sheet导出@PostMapping("/exports")public Long exports(){DataExportParam dataExportParam=new DataExportParam().setExportFileName("用户导出").setLimit(5);return excelService.doExport(dataExportParam,UserExportHandler.class, UserExportHandlerA.class);}

第一个sheet

@ExcelHandle
public class UserExportHandler implements ExportHandler {@AutowiredIUserService userService;@Overridepublic void init(ExcelContext ctx, DataParam param) {ExportContext context = (ExportContext) ctx;//此处的sheetNo会被覆盖,为了兼容一个文件多sheet导出WriteSheet sheet = EasyExcel.writerSheet(0, "第一个sheet").head(UserExportModel.class).build();context.setWriteSheet(sheet);}@Overridepublic ExportPage exportData(int startPage, int limit, DataExportParam dataExportParam) {IPage iPage = new Page<>(startPage, limit);IPage page = userService.page(iPage);List list = ExportListUtil.transform(page.getRecords(), UserExportModel.class);ExportPage result = new ExportPage<>();result.setTotal(page.getTotal());result.setCurrent(page.getCurrent());result.setSize(page.getSize());result.setRecords(list);return result;}@Overridepublic void beforePerPage(ExportContext ctx, DataExportParam param) {//分页执行,每页开始执行前}@Overridepublic void afterPerPage(List list, ExportContext ctx, DataExportParam param) {//分页执行,每页执行完成后}@Overridepublic void callBack(ExcelContext ctx, DataParam param) {//全部执行完成后回调}
}

第二个sheet

@ExcelHandle
public class UserExportHandlerA implements ExportHandler {@AutowiredIUserService userService;@Overridepublic void init(ExcelContext ctx, DataParam param) {ExportContext context = (ExportContext) ctx;//此处的sheetNo会被覆盖,为了兼容一个文件多sheet导出WriteSheet sheet = EasyExcel.writerSheet(0, "第二个sheet").head(UserExportModel.class).build();context.setWriteSheet(sheet);}@Overridepublic ExportPage exportData(int startPage, int limit, DataExportParam dataExportParam) {IPage iPage = new Page<>(startPage, limit);IPage page = userService.page(iPage);List list = ExportListUtil.transform(page.getRecords(), UserExportModel.class);ExportPage result = new ExportPage<>();result.setTotal(page.getTotal());result.setCurrent(page.getCurrent());result.setSize(page.getSize());result.setRecords(list);return result;}@Overridepublic void beforePerPage(ExportContext ctx, DataExportParam param) {//分页执行,每页开始执行前}@Overridepublic void afterPerPage(List list, ExportContext ctx, DataExportParam param) {//分页执行,每页执行完成后}@Overridepublic void callBack(ExcelContext ctx, DataParam param) {//全部执行完成后回调}
}

动态表头导入
表头不确定,需要业务上进行判断怎么办?
只要将你的导入实体继承至ImportRowMap即可

@Data
public class ImportRowMap extends ImportRow {@ExcelIgnoreprivate Map headMap;@ExcelIgnoreprivate Map dataMap;
}

这个类带了两个map 一个是表头的map,一个是数据的map
根据map自己去做对应的数据处理

示例代码

@ExcelHandle
public class DynamicHeadImportsHandler implements ImportHandler {@AutowiredIOplogService service;@Overridepublic List importData(List list, DataImportParam param)throws Exception {List errorMsgList = new ArrayList<>();//List oplogs = ExportListUtil.transform(list, Oplog.class);for (DynamicHeadImportsModel dynamicHeadImportsModel : list) {//处理固定列Oplog oplog = new Oplog();oplog.setOpUser(dynamicHeadImportsModel.getOpUser());oplog.setOpRemark(dynamicHeadImportsModel.getOpRemark());oplog.setOpSummary(dynamicHeadImportsModel.getOpSummary());oplog.setOpContent(dynamicHeadImportsModel.getOpContent());//模拟错误if (dynamicHeadImportsModel.getRow()%2==0){errorMsgList.add(new ErrorMsg(dynamicHeadImportsModel.getRow(),"2的倍数行出错"));}//处理动态列Map headMap = dynamicHeadImportsModel.getHeadMap();Map dataMap = dynamicHeadImportsModel.getDataMap();Map stringCellMap = mergeHeadAndData(headMap, dataMap);System.out.println(stringCellMap.keySet());}System.out.println("分页数据处理完成");return errorMsgList;}private Map mergeHeadAndData(Map headMap,Map dataMap){Set headKeySet = headMap.keySet();Map mergeMap=new LinkedHashMap<>();Iterator iterator = headKeySet.iterator();for (int i = 0; i Integer  key= iterator.next();mergeMap.put(headMap.get(key),dataMap.get(key));}return mergeMap;}//新增导入导出完成后的回调支持,如果多sheet导出时,也是整个生命周期完成后按顺序执行@Overridepublic void callBack(ExcelContext ctx, DataParam param) {//整个生命周期完成后执行一次该方法System.out.println("执行完成");}
}

导入入参说明

public class DataImportParam extends DataParam {/*** 输入流*/private InputStream stream;/*** 文件名称*/private String filename;/*** 导入对应的实体类*/private Class model;/*** 分批次大小,如果你导入1w条数据,每次1000会分10次读到内存中*/private int batchSize = 1000;/*** 是否限制导入行数,默认false,如果限制行数将会触发行数限制异常,例如限制1000行,你的文件如果超过1000行将会抛异常*/private boolean validMaxRows = false;/*** 行数限制validMaxRows=true时起作用*/private int maxRows = 1000;/*** 是否进行表头校验,顺序单元格内容都应该与实体类保持一致。*/private boolean validHead = true;}

导出入参说明

public class DataExportParam extends DataParam {/*** 分页大小*/private int limit=1000;/*** 导出文件名称*/private String exportFileName;/*** 写入excel的sheetName*/@Deprecatedprivate String sheetName;/*** 是否动态表头,默认false。*/@Deprecatedprivate boolean dynamicHead;/*** 当dynamicHead=true时需要传一个动态表头进来*/@Deprecatedprivate List> headList;/*** 表头对应的实体类*/@Deprecatedprivate Class headClass;/*** 自定义写处理器为了,自定义样式,表格合并之类的easyExcel原生扩展*/@Deprecatedprivate List writeHandlers;/*** 自定义类型转换器easyExcel原生扩展*/@Deprecatedprivate List> converters;
}
@Data
public class DataParam {//业务额外参数private Map parameters;//租户参数private String tenantCode;//用户参数private String createUserCode;//业务参数用于区分文件展示的不同模块private String businessCode;
}

相关内容

热门资讯

奥地利小镇邂逅农民思想家多伊布... 旅游奥地利期间,每天与同行的徐静波教授晨起一同散步,有时沿着多瑙河,有时在维也纳的森林间,但给我印象...
读懂济南|从“一阵风”到“长红... 齐鲁晚报·齐鲁壹点 王贝艺大明湖畔的狂欢还未收尾,五龙潭的泼水狂欢又已开场;广智里的咖啡香还没散尽,...
黄河壶口瀑布水量大增 形成壮美...   9月9日,受降雨天气影响,位于晋陕峡谷的黄河壶口瀑布水量大增,主副瀑布连成一片,形成壮美瀑布群景...
“请3休13”点燃中秋国庆国内... 财联社9月10日讯(记者 胡皓琼)中秋前一日火车票开售,中秋国庆国内游预订提速。财联社记者从OTA平...
微言|游客暴增却带不来利润?“... 中秋、国庆假期将至,很多地方将迎来旅游旺季。然而,一些地方旅游人次创新高,民宿经营者却普遍反映利润摊...