Java POI导出之数据验证

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,一起加油<< -_- >>

猜你喜欢

转载自blog.csdn.net/H1101370034/article/details/129048112