关于java:校验导入的excel表头以及表中内容为空

42次阅读

共计 4426 个字符,预计需要花费 12 分钟才能阅读完成。

问题背景:

在做一些 excel 导入需要时,尽管会有一些导入模版,但你永远不晓得用户是怎么用的,真正上传的是什么玩意。

可能存在的问题:

         1. 上传的文件后缀名称不是预期的
         2. 传了个空文件,表中内容为空
         3. 表头的内容或者程序不是预期的

有这样相似的问题时,须要咱们在程序里做提醒,做拦挡,防止不标准操作测验不严格而损坏了失常数据
应答形式:

在这里咱们应用 easyexcel 来进行 excel 数据的导入,具体应用配置如下:1. 引入依赖
            <!--easyexcel-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>2.2.6</version>
    </dependency>

2. 定义实体对象
@Data
public class RelatedPartyVO {
/**
 * 主键 id
 */
private Long id;
@ExcelProperty(value = "名称", index = 0) // 定义第一列的表头名称
private String name;
@ExcelProperty(value = "曾用名", index = 1) // 第二列
private String hisName;
@ExcelProperty(value = "类别", index = 2)
private String relatedType;
}

3. 定义监听器
@Slf4j
public class RelatedPartyListener extends AnalysisEventListener<RelatedPartyVO> {
/**
 * 每批读取的数据量
 */
private static final int BATCH_COUNT = 1000;
/**
 * 读取到的数据列表
 */
List<RelatedPartyVO> list = new ArrayList<>();
/**
 * 读取数据过程中的异样信息 map
 */
public Map<String, String> 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<Integer, String> headMap, AnalysisContext context) {super.invokeHeadMap(headMap, context);
        try {Map<Integer, String> indexMap = getIndexNameMap(RelatedPartyVO.class);
            if (indexMap == null || indexMap.size() < 1) {throw new ExcelAnalysisException("解析 Excel 出错,请按模版输出正确的 excel");
            }
            Set<Integer> 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<Integer, String> getIndexNameMap(Class clazz) throws NoSuchFieldException {Map<Integer, String> 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 = 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<String, String> 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);

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

正文完
 0