easy-excel通用异步导入导出神辅助

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

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

<dependency>
  <groupId>com.asyncexcel</groupId>
  <artifactId>async-excel-springboot-starter</artifactId>
  <version>1.1.0</version>
</dependency>

初始化数据库

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<UserExportModel> {
    
    @Autowired
    IUserService userService;
    
    @Override
    public 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);
    }
    
    @Override
    public ExportPage<UserExportModel> exportData(int startPage, int limit, DataExportParam dataExportParam) {
        IPage<User> iPage = new Page<>(startPage, limit);
        IPage page = userService.page(iPage);
        List<UserExportModel> list = ExportListUtil.transform(page.getRecords(), UserExportModel.class);
        ExportPage<UserExportModel> result = new ExportPage<>();
        result.setTotal(page.getTotal());
        result.setCurrent(page.getCurrent());
        result.setSize(page.getSize());
        result.setRecords(list);
        return result;
    }
    @Override
    public void beforePerPage(ExportContext ctx, DataExportParam param) {
        //分页执行每页开始执行前
    }
    
    @Override
    public void afterPerPage(List<UserExportModel> list, ExportContext ctx, DataExportParam param) {
        //分页执行每页执行完成后
    }
    
    @Override
    public void callBack(ExcelContext ctx, DataParam param) {
        //全部执行完成后回调
    }
}

第二个sheet

@ExcelHandle
public class UserExportHandlerA implements ExportHandler<UserExportModel> {
    
    @Autowired
    IUserService userService;
    
    @Override
    public 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);
    }
    
    @Override
    public ExportPage<UserExportModel> exportData(int startPage, int limit, DataExportParam dataExportParam) {
        IPage<User> iPage = new Page<>(startPage, limit);
        IPage page = userService.page(iPage);
        List<UserExportModel> list = ExportListUtil.transform(page.getRecords(), UserExportModel.class);
        ExportPage<UserExportModel> result = new ExportPage<>();
        result.setTotal(page.getTotal());
        result.setCurrent(page.getCurrent());
        result.setSize(page.getSize());
        result.setRecords(list);
        return result;
    }
    @Override
    public void beforePerPage(ExportContext ctx, DataExportParam param) {
        //分页执行每页开始执行前
    }
    
    @Override
    public void afterPerPage(List<UserExportModel> list, ExportContext ctx, DataExportParam param) {
        //分页执行每页执行完成后
    }
    
    @Override
    public void callBack(ExcelContext ctx, DataParam param) {
        //全部执行完成后回调
    }
}

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

@Data
public class ImportRowMap extends ImportRow {
    @ExcelIgnore
    private Map<Integer, String> headMap;
    @ExcelIgnore
    private Map<Integer, Cell> dataMap;
}

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

示例代码

@ExcelHandle
public class DynamicHeadImportsHandler implements ImportHandler<DynamicHeadImportsModel> {
    
    @Autowired
    IOplogService service;
    
    
    @Override
    public List<ErrorMsg> importData(List<DynamicHeadImportsModel> list, DataImportParam param)
        throws Exception {
        List<ErrorMsg> errorMsgList = new ArrayList<>();
        //List<Oplog> 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<Integer, String> headMap = dynamicHeadImportsModel.getHeadMap();
            Map<Integer, Cell> dataMap = dynamicHeadImportsModel.getDataMap();
            Map<String, Cell> stringCellMap = mergeHeadAndData(headMap, dataMap);
            System.out.println(stringCellMap.keySet());
        }
        System.out.println("分页数据处理完成");
        return errorMsgList;
    }
    
    private Map<String,Cell> mergeHeadAndData(Map<Integer, String> headMap,Map<Integer, Cell> dataMap){
        Set<Integer> headKeySet = headMap.keySet();
        Map<String,Cell> mergeMap=new LinkedHashMap<>();
        Iterator<Integer> iterator = headKeySet.iterator();
        for (int i = 0; i <headKeySet.size() ; i++) {
            Integer  key= iterator.next();
            mergeMap.put(headMap.get(key),dataMap.get(key));
        }
        return mergeMap;
    }
    
    //新增导入导出完成后的回调支持如果多sheet导出时也是整个生命周期完成后按顺序执行
    @Override
    public 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<T> extends DataParam {
    
    /**
     * 分页大小
     */
    private int limit=1000;
    /**
     * 导出文件名称
     */
    private String exportFileName;
    /**
     * 写入excel的sheetName
     */
    @Deprecated
    private String sheetName;
    /**
     * 是否动态表头默认false。
     */
    @Deprecated
    private boolean dynamicHead;
    /**
     * 当dynamicHead=true时需要传一个动态表头进来
     */
    @Deprecated
    private List<List<String>> headList;
    /**
     * 表头对应的实体类
     */
    @Deprecated
    private Class<?> headClass;
    /**
     * 自定义写处理器为了自定义样式表格合并之类的easyExcel原生扩展
     */
    @Deprecated
    private List<WriteHandler> writeHandlers;
    /**
     * 自定义类型转换器easyExcel原生扩展
     */
    @Deprecated
    private List<Converter<?>> converters;
}
@Data
public class DataParam {
    //业务额外参数
    private Map<String, Object> parameters;
    //租户参数
    private String tenantCode;
    //用户参数
    private String createUserCode;
    //业务参数用于区分文件展示的不同模块
    private String businessCode;
}
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6