Alibaba EasyExcel实现excel数据批量导入

简介: 最近在党建业务需求开发中,PD提出需要对党组织数据进行批量替换的需求。对需求分析理解后,我在项目中使用了Alibaba EasyExcel进行功能研发。

1.需求说明

通过党组织全称和上级党组织全称作为数据唯一key更新其它数据。

image.png

2.添加 EasyExcel 依赖

<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.1.6</version></dependency>

3.提供给前端的接口

publicLonguploadBatchUpdateOrgFile(@RequestParam("file") MultipartFilefile) throwsIOException {
Stringoperator=InvocationContexts.getContext().getUserId();
IccAssert.notNull(file, "上传的导入文件不能为空");
IccAssert.notEmpty(file.getOriginalFilename(), "上传的党组织文件名不能为空");
// 插入导入任务 ObExcelScheduleTasktaskParam=newObExcelScheduleTask();
// taskParam.set        ...
ObExcelScheduleTasktask=taskService.add(taskParam);
if (task==null) {
thrownewIccException(IccError.ICC_ERROR);
        }
InputStreaminputStream=file.getInputStream();
// 启用线程池异步导入数据ThreadPoolBusService.IMPORT_TASK_POOL.submit(newRunnable() {
@Overridepublicvoidrun() {
// 数据解析 实现AnalysisEventListenerOrgBatchUpdateExcelListenerorgBatchUpdateExcelListener=newOrgBatchUpdateExcelListener(commonDictService,
orgQueryService, taskService, tenantCode, file.getOriginalFilename(), orgBusinessId, task, partyUserService);
EasyExcel.read(inputStream, OrgBatchUpdateExcelModel.class, orgBatchUpdateExcelListener).sheet().doRead();
// 更新数据orgService.batchUpdateImportOrg(operator, tenantCode, file.getOriginalFilename(), task,
orgBatchUpdateExcelListener.getSuccessResultList(), orgBatchUpdateExcelListener.getFailResultList());
            }
        });
returntask.getId();
    }

5.讲解AnalysisEventListener< T>

监听器有哪些方法

publicabstractclassAnalysisEventListener<T>implementsReadListener<T> {
// 这是监听器的构造方法,一般我们可以通过构造方法传入一些我们需要在解析excel时使用的数据publicAnalysisEventListener() {}
// 调用invokeHeadMap来获取表头数据publicvoidinvokeHead(Map<Integer, CellData>headMap, AnalysisContextcontext) {
this.invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context), context);
    }
// 获取表头数据publicvoidinvokeHeadMap(Map<Integer, String>headMap, AnalysisContextcontext) {}
// 读取条额外信息:批注、超链接、合并单元格信息等publicvoidextra(CellExtraextra, AnalysisContextcontext) {}
// 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。publicvoidonException(Exceptionexception, AnalysisContextcontext) throwsException {throwexception;}
publicbooleanhasNext(AnalysisContextcontext) {returntrue;}
}


其中可以看到AnalysisEventListener 实现了 ReadListener

然后看到ReadListener后 发现其中还有两个方法是AnalysisEventListener没有实现的,而且这两个方法还是很重要的

publicinterfaceReadListener<T>extendsListener {
voidonException(Exceptionvar1, AnalysisContextvar2) throwsException;
voidinvokeHead(Map<Integer, CellData>var1, AnalysisContextvar2);
// 一行行读取表格内容voidinvoke(Tvar1, AnalysisContextvar2);
voidextra(CellExtravar1, AnalysisContextvar2);
// 读取完成后的操作voiddoAfterAllAnalysed(AnalysisContextvar1);
booleanhasNext(AnalysisContextvar1);
}

6.实现AnalysisEventListener过滤成功和失败的数据

publicclassOrgBatchUpdateExcelListenerextendsAnalysisEventListener<OrgBatchUpdateExcelModel> {
privateCommonDictServicecommonDictService;
privateOrgQueryServiceorgQueryService;
privateTaskServicetaskService;
privatePartyUserServicepartyUserService;
privateObExcelScheduleTasktask;
/*** 源文件名*/privateStringoriginalFilename;
/*** 组织编码*/privateStringorgBusinessId;
/*** 验证成功的数据*/privateList<OrgBatchUpdateExcelModel>successResultList;
/*** 验证失败的数据*/privateList<OrgBatchUpdateExcelModel>failResultList;
/*** 党组织类别*/privateMap<String, String>dzzlbRemarkMap;
/*** 党组织所在行政区划*/privateMap<String, String>xzqhMap;
/*** 党组织所在单位情况*/privateMap<String, String>dwqkMap;
/*** 党组织类别*/privateMap<String, String>dzzlbMap;
/*** 删除操作*/privatestaticfinalStringOPERATE_DELETE="删除";
/*** 修改操作*/privatestaticfinalStringOPERATE_UPDATE="修改";
privatestaticfinalStringEXCEL_HEAD_ERROR="文件不正确,请重新下载模板";
privatestaticfinalStringEXCEL_EMPTY_ERROR="文件里没有数据";
privatestaticfinalStringEXCEL_DATA_T00_LONG="excel里数据不能大于1000条";
/*** excel头*/privatestaticfinalStringHEAD="{0=党组织全称, 1=上级党组织全称, 2=党组织联系人, 3=党组织联系电话(手机号), 4=党组织类别, 5=党组织所在单位情况, 6=党组织所在行政区划, 7=主要业务, 8=操作}";
/*** excel头*/privatestaticfinalStringHEAD_TWO="{0=党组织全称, 1=上级党组织全称, 2=党组织联系人, 3=党组织联系电话(手机号), 4=党组织类别, 5=党组织所在单位情况, 6=党组织所在行政区划, 7=主要业务, 8=操作, 9=错误原因}";
/*** excel头*/privateMap<Integer, String>HEAD_MAP=null;
/*** 导入的起始行*/privatestaticfinalIntegerSTART_ROW_INDEX=3;
/*** 最大导入数量*/privatestaticfinalIntegerMAX_ROWS=1000;
/*** 租户*/privateStringtenantCode;
publicOrgBatchUpdateExcelListener(CommonDictServicecommonDictService, OrgQueryServiceorgQueryService, TaskServicetaskService,
StringtenantCode, StringoriginalFilename, StringorgBusinessId, ObExcelScheduleTasktask,
PartyUserServicepartyUserService) {
this.commonDictService=commonDictService;
this.orgQueryService=orgQueryService;
this.taskService=taskService;
this.tenantCode=tenantCode;
this.originalFilename=originalFilename;
this.orgBusinessId=orgBusinessId;
this.task=task;
this.partyUserService=partyUserService;
init();
    }
privatevoidinit() {
Map<String, String>dzzlbMap=commonDictService.getValueKeyByTypeCode(OrgConst.DICT_DZZ_ZZLB);
Map<String, String>dzzlbRemarkMap=commonDictService.getValueRemarkByTypeCode(OrgConst.DICT_DZZ_ZZLB);
Map<String, String>xzqhMap=commonDictService.getValueKeyByTypeCode(OrgConst.D_DZZ_XZQH);
Map<String, String>dwqkMap=commonDictService.getValueKeyByTypeCode(OrgConst.D_DZZ_DWQK);
this.dzzlbRemarkMap=dzzlbRemarkMap;
this.xzqhMap=xzqhMap;
this.dwqkMap=dwqkMap;
this.dzzlbMap=dzzlbMap;
this.successResultList=newArrayList<>();
this.failResultList=newArrayList<>();
    }
@OverridepublicvoidinvokeHeadMap(Map<Integer, String>headMap, AnalysisContextcontext) {
log.info("fileName:"+originalFilename);
// 验证excel是否合规context.readSheetHolder().setHeadRowNumber(START_ROW_INDEX);
HEAD_MAP=headMap;
introwIndex=context.readRowHolder().getRowIndex();
if (rowIndex==START_ROW_INDEX-1) {
Stringhead=String.valueOf(HEAD_MAP);
if (!StringUtils.equals(head, HEAD) &&!StringUtils.equals(head, HEAD_TWO)) {
log.info("head:{}", head);
thrownewExcelAnalysisException(EXCEL_HEAD_ERROR);
            }
inttotalRows=context.readSheetHolder().getApproximateTotalRowNumber();
if (totalRows<=START_ROW_INDEX) {
thrownewExcelAnalysisException(EXCEL_EMPTY_ERROR);
            }
if (totalRows-START_ROW_INDEX>MAX_ROWS) {
thrownewExcelAnalysisException(EXCEL_DATA_T00_LONG);
            }
        }
    }
@Overridepublicvoidinvoke(OrgBatchUpdateExcelModelorgBatchUpdateExcelModel, AnalysisContextanalysisContext) {
StringBuffererrorMsg=newStringBuffer();
introwIndex=analysisContext.readRowHolder().getRowIndex();
if (rowIndex>=START_ROW_INDEX) {
log.info("====当前数据========:{}", orgBatchUpdateExcelModel);
// 验证数据是否正确if (StringUtils.isBlank(orgBatchUpdateExcelModel.getOrgName())) {
errorMsg.append("|党组织全称不能为空");
            }
if (StringUtils.isBlank(orgBatchUpdateExcelModel.getParentOrgName())) {
errorMsg.append("|上级党组织全称不能为空");
            }
if (StringUtils.isBlank(orgBatchUpdateExcelModel.getOperate())) {
errorMsg.append("|操作列不能未空");
            } elseif (StringUtils.equals(orgBatchUpdateExcelModel.getOperate(), OPERATE_DELETE)) {
// 删除orgBatchUpdateExcelModel.setDeleteFlag(OrgConst.YES_STATE);
            } else {
// 更新Stringlinker=orgBatchUpdateExcelModel.getLinker();
StringlinkPhone=orgBatchUpdateExcelModel.getLinkPhone();
StringpartyTypeName=orgBatchUpdateExcelModel.getPartyTypeName();
StringpartyCompanyIntroName=orgBatchUpdateExcelModel.getPartyCompanyIntroName();
StringxzqhName=orgBatchUpdateExcelModel.getXzqhName();
StringmainBusiness=orgBatchUpdateExcelModel.getMainBusiness();
if (StringUtils.isBlank(linker) &&StringUtils.isBlank(linkPhone) &&StringUtils.isBlank(partyTypeName)
&&StringUtils.isBlank(partyCompanyIntroName) &&StringUtils.isBlank(xzqhName)
&&StringUtils.isBlank(mainBusiness)) {
errorMsg.append("|请至少输入一个修改项");
                }
if (StringUtils.isNotBlank(linkPhone)
&&!Validator.isMobile(linkPhone)) {
errorMsg.append("|党组织联系电话格式错误");
                }
if (StringUtils.isNotBlank(partyTypeName)) {
StringpartyType=dzzlbMap.get(orgBatchUpdateExcelModel.getPartyTypeName());
if (StringUtils.isBlank(partyType)) {
errorMsg.append("|党组织类别错误");
                    } else {
orgBatchUpdateExcelModel.setPartyType(partyType);
orgBatchUpdateExcelModel.setPartyTypeSimple(dzzlbRemarkMap.get(orgBatchUpdateExcelModel.getPartyTypeName()));
                    }
                }
if (StringUtils.isNotBlank(partyCompanyIntroName)) {
StringpartyCompanyIntro=dwqkMap.get(orgBatchUpdateExcelModel.getPartyCompanyIntroName());
if (StringUtils.isBlank(partyCompanyIntro)) {
errorMsg.append("|党组织所在单位情况错误");
                    } else {
orgBatchUpdateExcelModel.setPartyCompanyIntro(partyCompanyIntro);
                    }
                }
if (StringUtils.isNotBlank(xzqhName)) {
Stringxzqh=xzqhMap.get(orgBatchUpdateExcelModel.getXzqhName());
if (StringUtils.isBlank(xzqh)) {
errorMsg.append("|党组织所在行政区划错误");
                    } else {
orgBatchUpdateExcelModel.setXzqh(xzqh);
                    }
                }
            }
StringerrorMsgStr=errorMsg.toString();
if (StringUtils.isNotBlank(errorMsgStr)) {
orgBatchUpdateExcelModel.setErrorMsg(errorMsgStr);
this.clear(orgBatchUpdateExcelModel);
failResultList.add(orgBatchUpdateExcelModel);
            } else {
successResultList.add(orgBatchUpdateExcelModel);
            }
        }
    }
@OverridepublicvoiddoAfterAllAnalysed(AnalysisContextanalysisContext) {
if (CollectionUtils.isEmpty(successResultList) &&CollectionUtils.isEmpty(failResultList)) {
thrownewExcelAnalysisException(EXCEL_EMPTY_ERROR);
        }
if (!CollectionUtils.isEmpty(successResultList)) {
List<String>orgNameList=successResultList.stream().map(e->e.getOrgName()).collect(Collectors.toList());
Stringpids=orgQueryService.getPids(orgBusinessId, tenantCode);
List<ObOrgDTO>obOrgDTOList=orgQueryService.queryForBatchUpdateOrg(orgNameList, orgBusinessId, pids, tenantCode);
// map 用来获取组织编码Map<String, ObOrgDTO>map=obOrgDTOList.stream()
                    .collect(Collectors.toMap(ObOrgDTO::getOrgName, o->o, (v1, v2) ->v1));
// 上级党组织的信息List<String>pidList=obOrgDTOList.stream().map(e->e.getPid()).collect(Collectors.toList());
Map<String, ObPartyOrg>parentOrgMap=orgQueryService.batchQueryPartyOrg(pidList, tenantCode, OrgConst.NO_STATE);
// 本级党组织的信息List<String>currentOrgIdList=obOrgDTOList.stream().map(e->e.getOrgBusinessId()).collect(Collectors.toList());
Map<String, ObPartyOrg>currentOrgMap=orgQueryService.batchQueryPartyOrg(currentOrgIdList, tenantCode, OrgConst.NO_STATE);
booleanerrorFlag=false;
for (OrgBatchUpdateExcelModelmodel : successResultList) {
Stringkey=model.getOrgName() +model.getParentOrgName();
ObOrgDTOobOrgDTO=map.get(key);
if (obOrgDTO!=null) {
model.setOrgBusinessId(obOrgDTO.getOrgBusinessId());
if (StringUtils.equals(model.getPartyCompanyIntro(), OrgConst.IN_THE_SAME_COMPANY_CODE)) {
model.setCompanyBusinessId(parentOrgMap.get(obOrgDTO.getPid()) ==null?null : parentOrgMap.get(obOrgDTO.getPid()).getCompanyBusinessId());
model.setCompanyType(parentOrgMap.get(obOrgDTO.getPid()) ==null?null : parentOrgMap.get(obOrgDTO.getPid()).getCompanyType());
                    } else {
if (currentOrgMap.get(model.getOrgBusinessId()) !=null&&!StringUtils.equals(currentOrgMap.get(model.getOrgBusinessId()).getPartyCompanyIntro(), OrgConst.IN_THE_SAME_COMPANY_CODE)) {
model.setCompanyType(currentOrgMap.get(model.getOrgBusinessId()).getCompanyType());
model.setCompanyBusinessId(currentOrgMap.get(model.getOrgBusinessId()).getOrgBusinessId());
                        }
                    }
if (OrgConst.YES_STATE.equals(model.getDeleteFlag())) {
// 删除需要判断 下级有没有组织或党员List<String>childOrgBusinessId=orgQueryService.getAllChildOrgBusinessId(model.getOrgBusinessId(), tenantCode);
if (!CollectionUtils.isEmpty(childOrgBusinessId)) {
if (!currentOrgIdList.containsAll(childOrgBusinessId)) {
model.setErrorMsg("|该党组织下存在下级组织或党员数据");
this.clear(model);
failResultList.add(model);
errorFlag=true;
                            }
                        }
if(StringUtils.isBlank(model.getErrorMsg())){
PartyUserCriteriapartyUserCriteria=newPartyUserCriteria();
partyUserCriteria.setPid(model.getOrgBusinessId());
longuserCount=partyUserService.countPartyUser(partyUserCriteria);
if(userCount>0){
model.setErrorMsg("|该党组织下存在下级组织或党员数据");
this.clear(model);
failResultList.add(model);
errorFlag=true;
                            }
                        }
                    }
                } else {
model.setErrorMsg("|党组织全称或上级上组织全称错误");
this.clear(model);
failResultList.add(model);
errorFlag=true;
                }
            }
if (errorFlag) {
List<OrgBatchUpdateExcelModel>successList=successResultList.stream().filter(e->StringUtils.isBlank(e.getErrorMsg())).collect(Collectors.toList());
successResultList=newArrayList<>();
if (!CollectionUtils.isEmpty(successList)) {
successResultList.addAll(successList);
                }
            }
        }
    }
/*** 清楚不需要字段** @param model*/privatevoidclear(OrgBatchUpdateExcelModelmodel) {
model.setPartyTypeSimple(null);
model.setPartyType(null);
model.setDeleteFlag(null);
model.setXzqh(null);
model.setPartyCompanyIntro(null);
model.setCompanyType(null);
model.setOrgBusinessId(null);
model.setCompanyBusinessId(null);
    }
@OverridepublicvoidonException(Exceptionexception, AnalysisContextcontext) {
task.setTaskEnd(newDate());
task.setTaskStatus(EnumTaskStatus.FAIL);
task.setTaskErrorInfo(exception.getMessage());
taskService.updateStatusById(task);
    }
publicList<OrgBatchUpdateExcelModel>getSuccessResultList() {
returnsuccessResultList;
    }
publicList<OrgBatchUpdateExcelModel>getFailResultList() {
returnfailResultList;
    }

7.插入成功数据,上传失败数据到OSS供前端下载

/*** 导入文件-批量修改党组织** @param operator         操作人* @param tenantCode       租户* @param originalFilename 文件名* @param task             任务* @param successList      成功的数据* @param failList         失败的数据*/@Transactional(rollbackFor=Exception.class)
publicvoidbatchUpdateImportOrg(Stringoperator, StringtenantCode, StringoriginalFilename, ObExcelScheduleTasktask,
List<OrgBatchUpdateExcelModel>successList,
List<OrgBatchUpdateExcelModel>failList) {
task.setTaskStatus(EnumTaskStatus.DONE);
// 修改数据if (!CollectionUtils.isEmpty(successList)) {
try {
obPartyOrgMapper.updateBatchForImportOrg(successList, operator, tenantCode);
List<String>orgBusinessIdList=successList.stream().filter(e->OrgConst.YES_STATE.equals(e.getDeleteFlag())).map(e->e.getOrgBusinessId()).collect(Collectors.toList());
if(!CollectionUtils.isEmpty(orgBusinessIdList)){
obOrgMapper.batchDeleteObOrg(orgBusinessIdList, operator, newDate(), tenantCode);
                }
            } catch (Exceptione) {
task.setTaskStatus(EnumTaskStatus.FAIL);
task.setTaskErrorInfo(CommonUtil.stringAppend("生成和上传党组织批量修改错误文件时异常 errorId=", ErrorIdUtil.getErrorId(), " error=", ExceptionUtil.stacktraceToString(e, 150)));
            }
        }
// 上传错误数据文件到ossif (!CollectionUtils.isEmpty(failList)) {
InputStreamossInputStream=null;
try (ByteArrayOutputStreamoutputStream=newByteArrayOutputStream()) {
EasyExcel.write(outputStream, OrgBatchUpdateExcelVO.class).sheet(OrgConst.BATCH_UPDATE_EXCEL_SHEET).doWrite(failList);
ossInputStream=newByteArrayInputStream(outputStream.toByteArray());
//上传文件FileUploadResultresult=fileStorageService.uploadFile(ossInputStream, UploadResourceType.EXCEL, System.currentTimeMillis() +originalFilename);
if (result!=null) {
task.setTaskStatus(EnumTaskStatus.FAIL);
task.setOssFileUrl(result.getUrl());
task.setOssFileStatus(EnumOssFileStatus.NORMAL);
                }
            } catch (Exceptione) {
task.setTaskErrorInfo(CommonUtil.stringAppend("生成和上传党组织批量修改错误文件时异常 errorId=", ErrorIdUtil.getErrorId(), " error=", ExceptionUtil.stacktraceToString(e, 150)));
            }
        }
task.setTaskEnd(newDate());
taskService.updateStatusById(task);
    }
相关文章
|
1月前
|
数据采集 存储 JavaScript
自动化数据处理:使用Selenium与Excel打造的数据爬取管道
本文介绍了一种使用Selenium和Excel结合代理IP技术从WIPO品牌数据库(branddb.wipo.int)自动化爬取专利信息的方法。通过Selenium模拟用户操作,处理JavaScript动态加载页面,利用代理IP避免IP封禁,确保数据爬取稳定性和隐私性。爬取的数据将存储在Excel中,便于后续分析。此外,文章还详细介绍了Selenium的基本设置、代理IP配置及使用技巧,并探讨了未来可能采用的更多防反爬策略,以提升爬虫效率和稳定性。
|
3月前
|
关系型数据库 MySQL Shell
不通过navicat工具怎么把查询数据导出到excel表中
不通过navicat工具怎么把查询数据导出到excel表中
43 0
|
29天前
|
数据处理 Python
Python实用记录(十):获取excel数据并通过列表的形式保存为txt文档、xlsx文档、csv文档
这篇文章介绍了如何使用Python读取Excel文件中的数据,处理后将其保存为txt、xlsx和csv格式的文件。
44 3
Python实用记录(十):获取excel数据并通过列表的形式保存为txt文档、xlsx文档、csv文档
|
2月前
|
SQL C# 数据库
EPPlus库的安装和使用 C# 中 Excel的导入和导出
本文介绍了如何使用EPPlus库在C#中实现Excel的导入和导出功能。首先,通过NuGet包管理器安装EPPlus库,然后提供了将DataGridView数据导出到Excel的步骤和代码示例,包括将DataGridView转换为DataTable和使用EPPlus将DataTable导出为Excel文件。接着,介绍了如何将Excel数据导入到数据库中,包括读取Excel文件、解析数据、执行SQL插入操作。
EPPlus库的安装和使用 C# 中 Excel的导入和导出
|
1月前
|
easyexcel Java UED
SpringBoot中大量数据导出方案:使用EasyExcel并行导出多个excel文件并压缩zip后下载
在SpringBoot环境中,为了优化大量数据的Excel导出体验,可采用异步方式处理。具体做法是将数据拆分后利用`CompletableFuture`与`ThreadPoolTaskExecutor`并行导出,并使用EasyExcel生成多个Excel文件,最终将其压缩成ZIP文件供下载。此方案提升了导出效率,改善了用户体验。代码示例展示了如何实现这一过程,包括多线程处理、模板导出及资源清理等关键步骤。
|
2月前
|
数据采集 存储 数据挖掘
使用Python读取Excel数据
本文介绍了如何使用Python的`pandas`库读取和操作Excel文件。首先,需要安装`pandas`和`openpyxl`库。接着,通过`read_excel`函数读取Excel数据,并展示了读取特定工作表、查看数据以及计算平均值等操作。此外,还介绍了选择特定列、筛选数据和数据清洗等常用操作。`pandas`是一个强大且易用的工具,适用于日常数据处理工作。
|
3月前
|
SQL JSON 关系型数据库
n种方式教你用python读写excel等数据文件
n种方式教你用python读写excel等数据文件
|
3月前
|
存储 Java Apache
|
3月前
|
数据可视化 Python
我是如何把python获取到的数据写入Excel的?
我是如何把python获取到的数据写入Excel的?
45 2
|
3月前
|
索引 Python
Python基于Excel多列长度不定的数据怎么绘制折线图?
本文档详述了如何运用Python从CSV格式的Excel文件中读取特定范围的数据,并基于这些数据绘制多条折线图。文件的第一列代表循环增长的时间序列,后续各列包含不同属性的数据。通过指定起始与结束行数,可选取一个完整的时间循环周期内的数据进行绘图。每列数据以不同颜色和线型表示,并且图片长度会根据时间序列的长度动态调整,确保图表清晰易读。最终生成的图表将保存至指定文件夹。