当前位置: 首页 > news >正文

easyExcel 导入时,校验每个单元格数据

目录

1、定义excel导入文件对应的数据接收类 

2、定义属性转换器

3、定义数据解析监听器

4、解析文件


1、定义excel导入文件对应的数据接收类 
package com.ruoyi.project.domain.dto;import com.alibaba.excel.annotation.ExcelProperty;
import com.ruoyi.project.impot.convert.HeadUserExistConverter;
import com.ruoyi.project.impot.convert.ProjectNameConverter;
import com.ruoyi.project.impot.convert.ProjectNoConverter;
import lombok.Data;import java.math.BigDecimal;/*** 项目管理对象 t_project* * @author chance* @date 2024-07-24*/
@Data
public class ImportProjectDto {private static final long serialVersionUID = 1L;/*** $column.columnComment*/private Long id;/*** 项目名称*/@ExcelProperty(value = "项目名称",converter = ProjectNameConverter.class)private String projectName;/*** 项目编号*/@ExcelProperty(value = "项目编号",converter = ProjectNoConverter.class)private String projectNo;/*** 期初概算*/@ExcelProperty("期初概算")private BigDecimal initialBudgetEstimate;/*** 总概算*/@ExcelProperty("总概算")private BigDecimal generalEstimate;/*** 总概算*/@ExcelProperty("调整概算")private BigDecimal changeAmount;@ExcelProperty(value = "负责人",converter = HeadUserExistConverter.class)private String headUser;/*** 负责人id*/private Integer headUserId;
}

解释: 类中对“项目名称”、“项目编号”、 “负责人” 指定了转换器

ProjectNameConverter ,ProjectNoConverter ,HeadUserExistConverter

2、定义属性转换器

ProjectNameConverter.java 中的逻辑:

1、获取数据库中的项目名称集合(此集合是为了校验文件中项目名称是否和数据库中的项目名称重复)

2、定义excel中项目名称集合(每处理解析一行记录,则将项目名称加入集合,此集合是为了校验excel文件中项目名称是否重复)

如果1,2中的集合包含当前处理的数据记录,则抛出异常,这里异常会在后续自定义的listener中处理

package com.ruoyi.project.impot.convert;import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ReadConverterContext;
import com.alibaba.excel.converters.WriteConverterContext;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.project.mapper.ProjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;import java.util.LinkedList;
import java.util.List;@Slf4j
public class ProjectNameConverter implements Converter<String> {private List<String> dbProjectNameList = new LinkedList<>();private List<String> fileProjectNameList = new LinkedList<>();@Overridepublic Class<?> supportJavaTypeKey() {return String.class;}@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}/*** 这里读的时候会调用** @param context* @return*/@Overridepublic String convertToJavaData(ReadConverterContext<?> context)   {log.info("dbProjectNameList.size{}",dbProjectNameList.size());log.info("fileProjectNameList.size{}",fileProjectNameList.size());if(CollectionUtils.isEmpty(dbProjectNameList)){dbProjectNameList = SpringUtils.getBean(ProjectMapper.class).selectProjectNameList();}ReadCellData  readCellData= context.getReadCellData() ;String value = StringUtils.trim(readCellData.getStringValue());if (StringUtils.isNotEmpty(value)) {if(dbProjectNameList.contains(value)){throw new ExcelDataConvertException(readCellData.getRowIndex(),readCellData.getColumnIndex(),readCellData, context.getContentProperty(), "【"+ value + "】项目名称系统已存在");}if(fileProjectNameList.contains(value)){throw new ExcelDataConvertException(readCellData.getRowIndex(),readCellData.getColumnIndex(),readCellData, context.getContentProperty(),"文件中" + "【"+ value + "】项目名称编码重复");}fileProjectNameList.add(value);return value;}return null;}/*** 这里是写的时候会调用 不用管** @return*/@Overridepublic WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) {return new WriteCellData<>(context.getValue());}}
3、定义数据解析监听器
ProjectImportListener

这里的onException 方法会监听到转换器中抛出的异常,这里可以收集每条错误数据的原因,

其他方法不做赘述了。

package com.ruoyi.project.impot.listener;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.project.domain.Project;
import com.ruoyi.project.domain.dto.ImportProjectDto;
import com.ruoyi.project.mapper.ProjectMapper;
import com.ruoyi.project.mapper.ProjectHeadUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;/*** 读取转换异常** @author Jiaju Zhuang*/
@Slf4j
public class ProjectImportListener implements ReadListener<ImportProjectDto> {/*** 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收*/private static final int BATCH_COUNT = 100;private List<ImportProjectDto> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);private List<String> errorMsg = new LinkedList<>();private List<SysUser> userList = Lists.newArrayList();/*** 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。** @param exception* @param context* @throws Exception*/@Overridepublic void onException(Exception exception, AnalysisContext context) {log.error("解析失败,但是继续解析下一行:{}", exception.getMessage());// 如果是某一个单元格的转换异常 能获取到具体行号// 如果要获取头的信息 配合invokeHeadMap使用if (exception instanceof ExcelDataConvertException) {ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;log.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());errorMsg.add(String.format("第%s行,第%s列解析异常,数据为:%s,原因:%s", excelDataConvertException.getRowIndex()+1,excelDataConvertException.getColumnIndex()+1,excelDataConvertException.getCellData().getStringValue(),excelDataConvertException.getCause().getMessage()));}}/*** 这里会一行行的返回头** @param headMap* @param context*/@Overridepublic void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {log.info("解析到一条头数据:{}", JSON.toJSONString(headMap));}@Overridepublic void invoke(ImportProjectDto data, AnalysisContext context) {log.info("解析到一条数据:{}", JSON.toJSONString(data));cachedDataList.add(data);
//        if (cachedDataList.size() >= BATCH_COUNT) {
//            saveData();
//            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
//        }}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {saveData();log.info("所有数据解析完成!");}/*** 加上存储数据库*/private void saveData() {if (errorMsg.size() > 0 ){log.info("有%s条数据校验不通过,请检查", errorMsg.size());return;}log.info("{}条数据,开始存储数据库!", cachedDataList.size());ProjectMapper mapper =SpringUtils.getBean(ProjectMapper.class);ProjectHeadUserMapper userMapper =SpringUtils.getBean(ProjectHeadUserMapper.class);userList = userMapper.getUserList();Map<String,Long>  userNameIdMap = userList.stream().collect(Collectors.toMap(SysUser::getUserName, SysUser::getUserId));for (ImportProjectDto dto: cachedDataList) {Project project = new Project();BeanUtils.copyProperties(dto, project);project.setCreateBy(SecurityUtils.getUsername());project.setCreateTime(new Date());project.setGeneralEstimate(project.getInitialBudgetEstimate());project.setHeadUserId(userNameIdMap.get(dto.getHeadUser()).intValue());mapper.insertProject(project);}log.info("存储数据库成功!");}public List<String> getErrorMsg() {return errorMsg;}
}
4、解析文件
/*** @param file* @description: 导入项目* @return: com.ruoyi.common.core.domain.AjaxResult* @author 30864* @date: 2024/7/30 20:24*/@PostMapping("/importExcel")@PreAuthorize("@ss.hasPermi('project:project:import')")public AjaxResult importExcel(MultipartFile file) throws IOException {// 这里 需要指定读用哪个class去读,然后读取第一个sheetProjectImportListener listener = new ProjectImportListener();EasyExcel.read(file.getInputStream(), ImportProjectDto.class, listener).sheet().headRowNumber(1).doRead();List<String> errorMsg = listener.getErrorMsg();if (CollectionUtils.isNotEmpty(errorMsg)) {return AjaxResult.error(JSON.toJSONString(errorMsg), errorMsg);}return AjaxResult.success();}

总结:

此解析方法可以校验excel 中的每个单元格,且可以在listener 中获取到一行数据时,校验同行数据各属性间是否匹配,收集到错误数据,可以定位到行列号,具体实现可以自定义。

参考官网:读Excel | Easy Excel 官网 (alibaba.com)

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 如何快速判断Excel中选区跨页?
  • 笔记:应用Visual Studio Profiler识别和解决内存泄漏问题
  • UE4 BuildCookRun中的Archive的含义
  • 彩色相机拍照,图片时亮时暗
  • 数据结构(一)——顺序表和单向链表(一对一)
  • Magisk/Riru/LSPosed安装
  • SQLite的安装和使用
  • Java 对象拷贝复制,对象属性拷贝复制
  • 使用叶脊架构和EVPN-VXLAN技术增强数据中心性能
  • 基于深度学习的单目标跟踪系统
  • 跨境专线使用的是何种协议?为何网速很快?
  • Electron基础(二) 进程通信的ipcMain、contextBridge、ipcRenderer
  • jvm什么情况下会产生内存泄漏
  • 【0320】Postgres内核之 vacuum heap relation (15)
  • 米壳AI:做塞尔维亚跨境电商,用这个工具翻译产品主图,语言不再是难题!
  • 分享一款快速APP功能测试工具
  • bootstrap创建登录注册页面
  • CentOS6 编译安装 redis-3.2.3
  • Java精华积累:初学者都应该搞懂的问题
  • open-falcon 开发笔记(一):从零开始搭建虚拟服务器和监测环境
  • Sublime Text 2/3 绑定Eclipse快捷键
  • ViewService——一种保证客户端与服务端同步的方法
  • 阿里研究院入选中国企业智库系统影响力榜
  • 分享一份非常强势的Android面试题
  • 关于extract.autodesk.io的一些说明
  • 今年的LC3大会没了?
  • 马上搞懂 GeoJSON
  • 容器化应用: 在阿里云搭建多节点 Openshift 集群
  • 微信公众号开发小记——5.python微信红包
  • 一道闭包题引发的思考
  • 一道面试题引发的“血案”
  • 以太坊客户端Geth命令参数详解
  • 怎么把视频里的音乐提取出来
  • 整理一些计算机基础知识!
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • ​VRRP 虚拟路由冗余协议(华为)
  • ​批处理文件中的errorlevel用法
  • ###项目技术发展史
  • #《AI中文版》V3 第 1 章 概述
  • #pragma once
  • (1)(1.13) SiK无线电高级配置(六)
  • (超详细)2-YOLOV5改进-添加SimAM注意力机制
  • (二刷)代码随想录第16天|104.二叉树的最大深度 559.n叉树的最大深度● 111.二叉树的最小深度● 222.完全二叉树的节点个数
  • (九十四)函数和二维数组
  • (三)elasticsearch 源码之启动流程分析
  • (三)uboot源码分析
  • (十七)Flask之大型项目目录结构示例【二扣蓝图】
  • (十七)Flink 容错机制
  • (一)基于IDEA的JAVA基础1
  • (转)平衡树
  • ***详解账号泄露:全球约1亿用户已泄露
  • .axf 转化 .bin文件 的方法
  • .form文件_SSM框架文件上传篇
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .net on S60 ---- Net60 1.1发布 支持VS2008以及新的特性