问题背景:

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

可能存在的问题:

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

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

在这里咱们应用easyexcel来进行excel数据的导入,具体应用配置如下:1.引入依赖            <!--easyexcel-->    <dependency>        <groupId>com.alibaba</groupId>        <artifactId>easyexcel</artifactId>        <version>2.2.6</version>    </dependency>2.定义实体对象@Datapublic 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.定义监听器@Slf4jpublic 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中*/@Overridepublic 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();    }}/*** 读取表头的信息,对表头信息进行校验*/@Overridepublic 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);        }}/*** 分批次解析时,最初以批次解析实现 依据业务须要进行的操作*/@Overridepublic 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,对监听器的援用和异样信息的反馈    @Overridepublic 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);

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