Java POI导出之数据验证

news2024/10/1 9:48:45

Java POI导出之数据验证

maven 依赖

这里用的是apache.poi, 没有使用EasyExcel

<!--    poi依赖-->
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi</artifactId>
      <version>4.0.1</version>
    </dependency>
    <!--    poi对于excel 2007的支持依赖-->
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-ooxml</artifactId>
      <version>4.0.1</version>
    </dependency>
    <!--    poi对于excel 2007的支持依赖-->
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-ooxml-schemas</artifactId>
      <version>4.0.1</version>
    </dependency>

业务需求场景:

  1. 系统导入商户数据:一般Excel格式导入
  2. 系统导入会员数据:数据正确才导入,否则将数据错误信息写入上传的Excel中,并在前端网页进行倒出下载到用户电脑上,以便根据提示修改数据

    用户上传的Excel数据如下:属于异常数据

    在这里插入图片描述

    需求期望格式如下:

    在这里插入图片描述

上代码

  1. 上传接口类:ImportMemberController.java
    @RestController
    @RequestMapping
    public class ImportMemberController {
    
    	@Autowired
    	private IImportService importService;
    	
    	@PostMapping(value = "/import")
    	public void importDynamic(@RequestPart("file") MultipartFile file, HttpServletResponse response) throws IOException {
    		importService.validFormData(file, response);
    	}
    }
    
  2. 业务服务类:IImportService.java
    public interface IImportService throws IOException{
    	// 这里不做返回, 
    	// 业务需要可以在HttpServletResponse中自行添加返回,这里方便做记录
    	void validFormData(MultipartFile file, HttpServletResponse response);
    }
    
  3. 业务服务实现类:ImportServiceImpl.java
    @Service
    public class IImportService implements IImportService throws IOException{
    
    	/**
    	 * 校验数据,如果数据未按照约定格式,则给出相应错误提示,并导出下载到用户电脑上
    	 *
    	 **/
    	public void validFormData(MultipartFile file, HttpServletResponse response) {
    		Workbook wb = new XSSFWorkbook(file.getInputStream());
            Sheet sheet = wb.getSheetAt(0);
            /**
             * 不推荐使用以下方法,因为因为带有格式等问题,会出现空白行也算内容,
             * 导致行数超出,得到真实行数不准
             * int rowNums = wb.getSheetAt(0).getLastRowNum();
             * int rowNums = wb.getSheetAt(0).getPhysicalNumberOfRows();
             **/
    	    // 获取Excel 真是内容占用行数
            int rowNums = ExcelUtil.getVaildRows(wb);
        	int maxColumnNums = sheet.getRow(0).getLastCellNum() + 1;
        	// 所有存在合并单元格的行
       		 List<CellRangeAddress> mrList = sheet.getMergedRegions();
       		 // 标记Excel数据是否存在错误 
        	boolean isError = false;
        	// 这里2 是因为以上图中的数据是从第三行开始读取的,往下遍历
            for (int i = 2; i < rowNums; i++) {
                Row row = sheet.getRow(i);
                // 标记当前行是否存在错误
                boolean currentError = false;
                // 用来存储错误信息
                List<String> errorMessage = new LinkedList<>();
                // 遍历当前行每一个单元格
                for (int j = 0; j < maxColumnNums; j++) {
                    Cell cell = row.getCell(j);
                    String tableName = ExcelUtil.getCellValue(cell);
                    if (StringUtils.isBlank(tableName)) {
                        if (j == 0) {
                        	// 避免重复写入
                            if (!isError) {
                                isError = true;
                                sheet.getRow(2).createCell(maxColumnNums).setCellValue("错误信息");
                            }
                            boolean isValided = false;
                            for (CellRangeAddress cra : mrList) {
                                int firstRow = cra.getFirstRow();
                                int lastRow = cra.getLastRow();
                                if (i >= firstRow && i <= lastRow && lastRow - firstRow > 0) {
                                    Row mergedRow = sheet.getRow(cra.getFirstRow());
                                    String tableNameEn = ExcelUtil.getCellValue(mergedRow.getCell(0));
                                    if (StringUtils.isNotBlank(tableNameEn)) {
                                        isValided = true;
                                        break;
                                    }
                                }
                            }
                            if (!isValided) {
                                currentError = true;
                                errorMessage.add("门店名称不能为空");
                            }
                        } else if (j == 2){
                            currentError = true;
                            errorMessage.add("门店编号不能为空");
                        } else if (j == 3){
                            currentError = true;
                            errorMessage.add("会员姓名不能为空");
                        } else if (j == 4){
                            currentError = true;
                            errorMessage.add("会员手机号不能为空");
                        }  else if (j == 5){
                            currentError = true;
                            errorMessage.add("是否新客不能为空");
                        }
                    }
                }
                // 当前行存在错误,创建写入错误的列,并写入错误信息
                if (currentError)  {
                	// 设置写入错误信息所在列宽度自适应
                    sheet.autoSizeColumn(maxColumnNums);
                    sheet.setColumnWidth(maxColumnNums,sheet.getColumnWidth(maxColumnNums)*17/10);
                    // 写入错误信息
                    AtomicInteger index = new AtomicInteger(1);
                    row.createCell(maxColumnNums)
                    .setCellValue(errorMessage.stream()
                    .map(x-> index.getAndIncrement() + "." + x).collect(Collectors.joining(", ")));
                }
            }
            // 如果文件中存在任何一处数据校验错误,则将Excel文件返回前端进行下载
            if (isError) {
                wb.write(response.getOutputStream());
            }
    	}
    }
    
  4. 工具类:ExcelUtil
    public class ExcelUtil { 
    	/**
    	 * 
    	 * @ReturnType String
    	 * @Description 获取单元格内容
    	 * @param cell 单元格
    	 * @return
    	 */
    	public static String getCellValue(Cell cell) {
    		if (cell == null) {
    			return "";
    		}
    		CellType type = cell.getCellType();
    		if (type == CellType.STRING) {
    			return cell.getStringCellValue();
    		} else if (type == CellType.BOOLEAN) {
    			return String.valueOf(cell.getBooleanCellValue());
    		} else if (type == CellType.FORMULA) {
    			return cell.getCellFormula();
    		} else if (type == CellType.NUMERIC) {
    			double valuedouble = cell.getNumericCellValue();
    			int valueint = (int)valuedouble;
    			if(valueint == valuedouble) {
    				return String.valueOf(valueint);
    			}else {
    				return String.valueOf(valuedouble);
    			}
    		}
    		return "";
    	}
    }
    
     /**
      * 
      * @ReturnType int
      * @Description 获取Excel 真实内容行数
      * @param Workbook 文档模板
      * @return
      */
    public static int getVaildRows(Workbook wb) {
    	Sheet sheet = wb.getSheetAt(0);
        CellReference cellReference = new CellReference("A4");
        boolean flag = false;
        for (int i = cellReference.getRow(); i <= sheet.getLastRowNum(); ) {
            Row r = sheet.getRow(i);
            if (r == null) {
                // 如果是空行(即没有任何数据、格式),直接把它以下的数据往上移动
                sheet.shiftRows(i + 1, sheet.getLastRowNum(), -1);
                continue;
            }
            flag = false;
            for (Cell c : r) {
                if (c.getCellType() != CellType.BLANK) {
                    flag = true;
                    break;
                }
            }
            if (flag) {
                i++;
                continue;
            } else {
                //如果是空白行(即可能没有数据,但是有一定格式)
                if (i == sheet.getLastRowNum()){
                    //如果到了最后一行,直接将那一行remove掉
                    sheet.removeRow(r);
                } else {
                    //如果还没到最后一行,则数据往上移一行
                    sheet.shiftRows(i + 1, sheet.getLastRowNum(), -1);
                }
            }
        }
        return sheet.getLastRowNum() + 1;
    }
    

注意:上传接口类:ImportMemberController.java

  1. 在上传接口 importDynamic 里我没有直接 return 做返回,这是因为如果存在错误时会写文件到前端,
    那么在此再做返回的话, 接口处返回的数据会混在文件内容里,导致文件格式错误, Office 打开时报文件格式错误或者损坏,无法打开, WPS应该可以,那玩意儿强大的一批
    所以在此也提醒前端的朋友们,如果下载后台返回的Excel模板后,Office 打开报格式错误,那么可以从浏览器Network中查看请求得到的文件数据,中间数据看不懂没关系,直接拉到最后,看看数据最后面是不是追加了有不属于Excel模板的数据, 去掉后就可以正常打开了
    如果能让用户都是用WPS的请无视

做个记录,如果错误,欢迎大佬指正,如果问题可留言,2023,一起加油<< -_- >>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/348365.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何运维多集群数据库?58 同城 NebulaGraph Database 运维实践

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SktQW2qn-1676450580889)(https://www-cdn.nebula-graph.com.cn/nebula-website-5.0/images/blogs/58.%20Com%20Inc/58%E5%90%8C%E5%9F%8E_%E7%94%BB%E6%9D%BF%201.jpg)] 图计算业务背景介绍 我们为什…

jsp游戏门户网站系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 jsp 游戏门户网站系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql&#xff0c;使…

简单易用的图片缩略图处理库

哈喽&#xff0c;大家好&#xff0c;我是源小北。 最近在工作中&#xff0c;刚好有遇到对图片进行缩放处理的功能。于是&#xff0c;便拿起很久以前使用过的Java原生工具类处理图片&#xff0c;不过在开发过程中也发现了一些问题。 在Java中&#xff0c;原生的处理图片缩略图…

浅聊一下,可中断锁(ReentrantLock)

前言 今天早上上厕所&#xff0c;上的我痔疮犯了&#xff0c;屁股一坐下去就感觉一根针在刺我&#xff0c;得的是外痔&#xff0c;之前还坚持用痔疮膏来着&#xff0c;但是感觉涂药的那个姿势以及位置我实在无法忍受&#xff0c;就把它给断了&#xff0c;到头来还是屁股糟了罪&…

工程监测多通道振弦模拟信号采集仪VTN参数修改

工程监测多通道振弦模拟信号采集仪VTN参数修改 1 使用按键修改参数 使用按键修改某个参数的方法如下&#xff1a; &#xff08;1&#xff09;在系统参数查看页面&#xff08;PXX 页面&#xff09;&#xff0c;按【SWITCH】或【SETTING】按键切换到要修改的参数项。 &#xff08…

蓝库云|8项关键让你看透企业「数字转型」,零代码是惊喜

各行各业都要面对的多方竞争力及不断上涨的经营成本&#xff0c;以及随着时代的发展&#xff0c;有不少企业纷纷推动数字化转型&#xff0c;考虑藉着应用现代化的数据和科技工具的结合&#xff0c;协助企业创造新的营运模式及收入来源&#xff0c;以提升自动化效率、优化客户体…

用于非线性时间序列预测的稀疏局部线性和邻域嵌入(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

SpringBoot 文件上传后查看404的问题和解决404后需要访问两次才能查看的问题

文件上传、图片上传的实现见这个&#xff1a; SpringBootVue 实现头像上传功能_Teln_小凯的博客-CSDN博客 在实现上面的功能后&#xff0c;发现查看图片的时候提示404&#xff0c;解决这个方法如下&#xff1a; 1、配置资源静态文件映射 第一个参数是页面请求的地址&#x…

ChatGPT 整合到Laravel项目中使用

之前写的《ChatApI 本地如何调用》是直接下载ChatGPT SDK当做单独项目开发使用的&#xff0c;这次写的是整合到当前项目中使用ChatGPT 合并为一个项目去使用&#xff1b;具体操作方法也有一定区别。 目录 1.安装ChatGPT SDK 2.加载ChatGPT类库 3.安装guzzlehttp 4.代码实操…

【近似算法】—0-1背包问题的近似算法

【近似算法】—0-1背包问题的近似算法Approximation Schemes(近似方案)PTAS&#xff08;Polynomial time approximation scheme&#xff09;定义&#xff1a;FPTAS&#xff08;Fully polynomial time approximation scheme&#xff09;定义&#xff1a;PPTAS&#xff08;Pseudo…

浏览器F12功能详细介绍

哈喽&#xff0c;大家好&#xff0c;我是有勇气的牛排&#xff08;全网同名&#xff09;&#x1f42e; 有问题的小伙伴欢迎在文末评论&#xff0c;点赞、收藏是对我最大的支持&#xff01;&#xff01;&#xff01;。 掌握浏览器调试基本属于开发者必备的技能&#xff0c;熟悉…

Unity 2021 LTS中着色器构建时间和内存使用的改进

随着Unity的可脚本渲染管道(SRP)的可用特性集的不断增长&#xff0c;在构建时处理和编译的着色器变量的数量也在不断增加。除了对更多图形api的持续支持和不断增长的目标平台选择外&#xff0c;SRP的改进还在继续扩展。着色器在初始(“干净”)构建后被编译和缓存&#xff0c;从…

自动曝光在移动平台上的实现方案——以《使命召唤手游》为例

一. Intro在PBR渲染中&#xff0c;除了已被大家深入分析了很多遍的PBR材质属性&#xff08;Surface Appearance&#xff09;外&#xff0c;合理的光源强度和后处理也是不可或缺的部分。这里结合工作中的一些实践经验&#xff0c;讨论一下后处理中另一个关键环节——自动曝光在移…

Consul SpringCloudK8S

背景说起微服务&#xff0c;就需要用到SpringCloud&#xff0c;目前市面上主流的SpringCloud产品有这些&#xff1a;SpringCloudNeflix、Spring Cloud Alibaba、Spring Cloud for AWS、Spring Cloud Azure 和 Spring Cloud Kubernetes。其中SpringCloudNeflix已经不在更新&…

C#开发记录——C#开发的OPC客户端无法连接杰控软件的OPC服务器处理办法

1、某些OPC 客户端连接本地服务器失败 某些OPCDa 客户端,例如ABBDCS,.WinCC 等,本地连接服务器失败,需特殊设置; C#开发的就遇到此情况&#xff0c;排查原因&#xff0c;停滞了好几天&#xff0c;终于解决 1.1、打开DCOM 配置,64 位系统需执行: 1.2、从组件服务->计算机-&…

iOS UI自动化测试详解

前言&#xff1a; 小目标 关于UI自动化的定义&#xff0c;我想要的是自动地按照流程去点击页面、输入数据&#xff0c;不需要人去参与&#xff0c;节省人工时间。比如登录&#xff0c;能够自己去填写用户名&密码&#xff0c;然后点击按钮跳转到下一个页面等。在能够保证业…

pandas表格并表(累加合并)

今天需求是用pandas的两张表格合并起来&#xff0c;其中重复的部分将数据进行相加。 用到的是combine&#xff08;&#xff09;这个函数。 函数详细的使用可以看这个大佬的文章&#xff1a; https://www.cnblogs.com/traditional/p/12727997.html &#xff08;这个文章使用的测…

vue之为什么data属性是一个函数而不是一个对象?

为什么data属性是一个函数而不是一个对象为什么data属性是一个函数而不是一个对象&#xff1f;一、实例和组件定义data的区别二、组件data定义函数与对象的区别三、原理分析四、结论为什么data属性是一个函数而不是一个对象&#xff1f; 一、实例和组件定义data的区别 vue实例…

嵌入式物联网【数据处理篇】特殊字符处理(Postman 400 Bad Request)

目录 一、Postman 400 Bad Request 二、C语言特殊字符处理 三、QUrl toPercentEncoding 处理url中含有特殊字符转码 16进制ASCII码对照表 一、Postman 400 Bad Request http请求链接中的特殊字符处理 解决包括Postman 中的post、get等链接请求中的特殊字符&#xff08;如…

谷歌关键词优化多少钱【2023年调研】

本文主要分享Google关键词排名优化的一些成本调研&#xff0c;方便大家参考。 本文由光算创作&#xff0c;有可能会被剽窃和修改&#xff0c;我们佛系对待这种行为吧。 今年2023年了&#xff0c;谷歌关键词优化到底要多少钱&#xff1f; 答案是&#xff1a;价格在2w~25w左右…