校验导入的excel表头以及表中内容为空

【校验导入的excel表头以及表中内容为空】问题背景:

在做一些excel导入需求时,虽然会有一些导入模版,但你永远不知道用户是怎么用的,真正上传的是什么玩意。

可能存在的问题:
1.上传的文件后缀名称不是预期的 2.传了个空文件,表中内容为空 3.表头的内容或者顺序不是预期的

有这样类似的问题时,需要我们在程序里做提示,做拦截,避免不规范操作检验不严格而损坏了正常数据
应对方式:
在这里我们使用easyexcel来进行excel数据的导入,具体使用配置如下: 1.引入依赖 com.alibaba easyexcel 2.2.6 2.定义实体对象 @Data public class RelatedPartyVO { /** * 主键id */ private Long id; @ExcelProperty(value = "https://www.it610.com/article/名称", index = 0) //定义第一列的表头名称 private String name; @ExcelProperty(value = "https://www.it610.com/article/曾用名", index = 1) //第二列 private String hisName; @ExcelProperty(value = "https://www.it610.com/article/类别", index = 2) private String relatedType; }3.定义监听器 @Slf4j public class RelatedPartyListener extends AnalysisEventListener { /** * 每批读取的数据量 */ private static final int BATCH_COUNT = 1000; /** * 读取到的数据列表 */ List list = new ArrayList<>(); /** * 读取数据过程中的异常信息map */ public Map map = new HashMap<>(); /** * 读取的文件是否是空的标识 true是空 false不是空 */ public boolean blankFlag = true; /** * 引入要进行具体操作的服务类 */ private final RelatedPartyService relatedPartyService; public RelatedPartyListener(RelatedPartyService relatedPartyService) { this.relatedPartyService = relatedPartyService; }/** * 每读取一行,对数据的具体业务判断,异常提示放入map中 */ @Override public void invoke(RelatedPartyVO t, AnalysisContext analysisContext) { blankFlag = false; if (StringUtils.isBlank(t.getName())) { map.put("code", String.valueOf(ApiResponseCode.ERROR_202.getCode())); map.put("msg", "导入数据表中未填写名称信息"); return; } list.add(t); if (list.size() >= BATCH_COUNT) { relatedPartyService.saveBatch(list); list.clear(); } } /** * 读取表头的信息,对表头信息进行校验 */ @Override public void invokeHeadMap(Map headMap, AnalysisContext context) { super.invokeHeadMap(headMap, context); try { Map indexMap = getIndexNameMap(RelatedPartyVO.class); if (indexMap == null || indexMap.size() < 1) { throw new ExcelAnalysisException("解析Excel出错,请按模版输入正确的excel"); } Set keySet = indexMap.keySet(); for (Integer key : keySet) { if (StringUtils.isBlank(headMap.get(key))) { throw new ExcelAnalysisException("解析Excel出错,请按模版输入正确的excel"); } if (!headMap.get(key).equals(indexMap.get(key))) { throw new ExcelAnalysisException("解析Excel出错,请按模版输入正确的excel"); } } } catch (NoSuchFieldException e) { log.error("invokeHeadMap error", e); } } /** * 分批次解析时,最后以批次解析完成 根据业务需要进行的操作 */ @Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { relatedPartyService.saveBatch(list); log.info("所有数据解析完成,开始移除失效数据"); }/** * 获取excel表头的内容,放入map,方便根据实体类对表头内容进行校验 * * @param clazz 表头名称 * @return 导入对象备注map * @throws NoSuchFieldException */ public Map getIndexNameMap(Class clazz) throws NoSuchFieldException { Map result = new HashMap<>(); Field field; Field[] fields = clazz.getDeclaredFields(); for (Field item : fields) { field = clazz.getDeclaredField(item.getName()); field.setAccessible(true); ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); if (excelProperty != null) { int index = excelProperty.index(); String[] values = excelProperty.value(); StringBuilder value = https://www.it610.com/article/new StringBuilder(); for (String v : values) { value.append(v); } result.put(index, value.toString()); } } return result; } }4.读取excel,对监听器的引用和异常信息的反馈 @Override public void uploadRelatedParty(File tempFile) { try { log.info("uploadRelatedParty 导入excel列表,更新当前库中的关联方状态为失效"); updateAllRelatedStatusTo(StatusEnum.DISABLE.getCode()); RelatedPartyListener partyListener = new RelatedPartyListener(this); EasyExcel.read(tempFile.getAbsolutePath(), RelatedPartyVO.class, partyListener) .sheet() .doRead(); Map map = partyListener.map; if (map.size() != 0) { throw new RuntimeException(map.get("msg")); } if (partyListener.blankFlag) { throw new RuntimeException("客户端上传的空文件,回滚数据"); } log.info("写入新数据完成,移除失效数据"); deleteRelatedByStatus(StatusEnum.DISABLE.getCode()); } catch (Exception e) { log.error("导入excel出错,数据回滚后,删除新导入的标示为有效的数据,再将所有历史数据更新为有效", e); deleteRelatedByStatus(StatusEnum.ENABLE.getCode()); updateAllRelatedStatusTo(StatusEnum.ENABLE.getCode()); throw new RuntimeException(e); } finally { boolean delete = tempFile.delete(); log.info("uploadRelatedParty delete file result is [{}]", delete); } }5.上传excel,校验文件后缀 大小等内容 //判断导入的是否为excel文件 String fileName = file.getOriginalFilename(); if (!(fileName.endsWith(ExcelTypeEnum.XLS.getValue()) || fileName.endsWith(ExcelTypeEnum.XLSX.getValue()))) { return getErrorJsonDto(ApiResponseCode.ERROR_202, "只接受Excel类型的文件!"); } if (AppUtil.checkFileSize(file.getSize(), FILE_SIZE, FILE_UNIT)) { return getErrorJsonDto(ApiResponseCode.ERROR_202, "导入文件大于10M,已超限"); } //写入临时文件 File tempFile = new File("/tmp/mdm_related_party_info_" + System.currentTimeMillis() + ".xlsx"); file.transferTo(tempFile); //读取文件 relatedPartyService.uploadRelatedParty(tempFile);

注意点:如果是分布式服务的话,在读取文件前,需要先加上分布式锁

    推荐阅读