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

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;
}

相关内容

热门资讯

恶魔在身边,这部片大慨是讲什么... 恶魔在身边,这部片大慨是讲什么,大结局是怎么样的,可以在哪里看到这就是中国结啊。打字很难说的清楚。首...
芭比梦幻屋冒险旅程的主题曲叫什... 芭比梦幻屋冒险旅程的主题曲叫什么名,可爱的芭比嘟嘟嘟。这就是它的主题曲名字。翻译过来就这么好听。你听...
魏子玉是什么人 魏子玉是什么人魏子玉是什么人魏东亭的孙子
男孩2015年出生,姓李可起名... 男孩2015年出生,姓李可起名按辈份,中间带吉子不知起什么名好文澜:文章的波澜。适用于男孩和女孩取名...
好人定义是什么? 好人定义是什么?我理解的好人,是大方向大原则方面的好人。 虽然不以恶小而为之的多半是好人, 但要是有...
如何评价《道士下山》这部电影,... 如何评价《道士下山》这部电影,我是完全没看懂不谙世事的小道士,以最朴实的理由踏入红尘———闹粮荒。最...
作为职场新人,被同事欺负了怎么... 作为职场新人,被同事欺负了怎么办呢?第一点弄清楚他为什么欺负你;第二点如果他对你有误会,可能只是表达...
柳毅传作者简介 柳毅传作者简介李朝威〔唐〕(约公元七五九年前后在世)字不详,陇西人。生卒年均不祥,约唐肃宗乾元中前后...
只是哪个动漫里的人物? 只是哪个动漫里的人物?是我们的存在吧,不过剧情些许虐心吧,不要被这张图的美好景象给骗了我们的存在这动...
寻一小说:情景和召唤红警差不多... 寻一小说:情景和召唤红警差不多的。内容呢?我看过本《异界虫族》到是和召唤红警差不多,主角跑到异界召唤...
死神TV原创内容有些什么? 死神TV原创内容有些什么?魂狩篇(064-091,原创剧情篇) 魂狩·尸魂界强袭篇(092-109,...
你会跟年龄差七岁的人在一起嘛 你会跟年龄差七岁的人在一起嘛如果我很喜欢这个人对方也喜欢我,我会跟年龄差七岁的人在一起,只要相爱,那...
一个完全不懂爱情的女孩子 要怎... 一个完全不懂爱情的女孩子 要怎样才能看出她喜欢上了一个人感觉是种很微妙的东西,不知不觉当中你或许就已...
军训是什么? 军训是什么?军训是学校开学的 必备课 ,有锈的锻炼同学们的意志 让他们积极乐观 学习 乐于助人的精神
想写小说,可文采一般,问怎样才... 想写小说,可文采一般,问怎样才能提高自己的文采推荐一些可提高文笔的书看,和写小说的注意事项多看一些文...
黑暗浮士德那一集死的 黑暗浮士德那一集死的凹凸曼。。。什么鬼   --大脑是好的,真希望每个人都有去黑暗浮士德吧,这里的貌...
推荐几本好看的探险小说 推荐几本好看的探险小说答:《我只偷上流社会》虽然不能完全算上探险类,不过也不错
玄幻小说《无双界》!是修武小说... 玄幻小说《无双界》!是修武小说的开山之作吗?不是。修武是啥我不知道,但《无双界》是去年的新书。修仙的...
,心开头的成语接龙 ,心开头的成语接龙心开头的成语接龙:心想事【成】竹在【胸】有成【竹】报平【安】步当【车】水马【龙】飞...
星际争霸2战网点数怎么充值? 星际争霸2战网点数怎么充值?实卡 叫什么卡 一张多少点 要多少张才能终身魔兽世界实体充值卡通用,1元...