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

ops/2024/9/23 14:26:07/

目录

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

2、定义属性转换器

3、定义数据解析监听器

4、解析文件


1、定义excel导入文件对应的数据接收类 
java">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中处理

java">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 方法会监听到转换器中抛出的异常,这里可以收集每条错误数据的原因,

其他方法不做赘述了。

java">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、解析文件
java">/*** @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)


http://www.ppmy.cn/ops/103323.html

相关文章

基于live555开发的多线程RTSPServer轻量级流媒体服务器EasyRTSPServer开源代码及其调用示例,PC、嵌入式、Android全通用

EasyRTSPServer参考live555 testProg中的testOnDemandRTSPServer示例程序&#xff0c;将一个live555 testOnDemandRTSPServer封装在一个类中&#xff0c;例如&#xff0c;我们称为Class EasyRTSPServer&#xff0c;在EasyRTSPServer_Create接口调用时&#xff0c;我们新建一个E…

自动化01:认识接线端子

1、TB接线端子&#xff1a; 2.SAK组合接线端子&#xff1a; 3.UK组合接线端子&#xff1a; 4.插拔式接线端子&#xff1a; 5.零线接线桩&#xff1a; 6.零线接线排

去中心化(Decentralization)

去中心化&#xff08;Decentralization) 并不是一个新概念&#xff0c;它已在战略、管理和政府中使用了很长时间。去中心化的基本思想是将控制权和权限分配给组织的外围&#xff0c;而不是由一个中心机构完全控制组织。这种配置为组织带来了许多好处&#xff0c;例如提高了效率…

探索Python的Excel力量:openpyxl库的奥秘

文章目录 探索Python的Excel力量&#xff1a;openpyxl库的奥秘背景&#xff1a;为什么选择openpyxl&#xff1f;库简介&#xff1a;openpyxl是什么&#xff1f;安装指南&#xff1a;如何安装openpyxl&#xff1f;快速上手&#xff1a;五个基本函数实战演练&#xff1a;三个应用…

Python操作Excel的2024年新框架zdppy_excel,使用简单,零依赖,国产

zdppy_excel Python处理Excel的专属工具库 特性 1、零依赖2、国产框架&#xff0c;有完整的中文文档3、使用简单4、低代码开发 安装 pip install zdppy_excel使用教程 写入数据 from zdppy_excel import Workbook import datetime# 创建一个 workbook wb Workbook()# 获…

彩色相机拍照,图片时亮时暗

在自动化设备的调式过程中&#xff0c;发现相机拍照的图片时亮时暗&#xff0c;于是排查问题&#xff1a; 1&#xff0c;是否拍照的时机不对&#xff1f; 2&#xff0c;是否有东西挡住相机了&#xff1f; 3&#xff0c;光源打开的时间是否合适&#xff1f;是否有延时&#x…

Web攻击-XSS、CSRF、SQL注入

1. XSS攻击&#xff1a;跨站脚本攻击 XSS&#xff08;Cross Site Scripting&#xff09;&#xff0c;跨站脚本攻击&#xff0c;允许攻击者将恶意代码植入到提供给其它用户使用的页面中。 XSS的攻击目标是为了盗取存储在客户端的cookie或者其他网站用于识别客户端身份的敏感信…

六个方面探讨企业为何迫切需要替换FTP

随着信息技术的迅猛发展和网络安全形势的日趋严峻&#xff0c;传统的FTP文件传输协议因其固有的局限性和安全隐患&#xff0c;逐渐难以满足现代企业的需求。因此&#xff0c;寻找并升级FTP替代方案成为众多企业需要解决的问题。本文将从六个方面探讨企业为何迫切需要替换FTP。 …