XLSX解析器优化需求:无需指定列序号处理动态列
动态识别列的XLSX解析优化方案
问题分析
原代码存在两个核心问题:
- 硬编码列索引(
0、1),当Excel列顺序调整或列缺失时,直接导致解析失败 - 重复编写遍历逻辑,每新增一列就要复制一套循环,代码冗余度极高
优化方案
通过先解析表头建立列名与索引的映射,再统一遍历数据行动态获取列数据,同时增加单元格类型兼容处理,实现灵活适配不同结构的Excel文件。
优化后代码
public ResponseEntity<Object> importPcpXlsx(MultipartFile xlsx) { try { XSSFWorkbook xssfWorkbook = new XSSFWorkbook(xlsx.getInputStream()); XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); if (xssfSheet.getRow(0) == null) { // 处理空表头的情况 return ResponseEntity.badRequest().body("Excel表头不能为空"); } // 1. 构建表头映射:列名 -> 列索引 Map<String, Integer> headerMap = new HashMap<>(); Row headerRow = xssfSheet.getRow(0); for (int colIndex = 0; colIndex < headerRow.getLastCellNum(); colIndex++) { Cell headerCell = headerRow.getCell(colIndex); if (headerCell != null) { String headerName = getCellStringValue(headerCell).trim(); if (!headerName.isEmpty()) { headerMap.put(headerName, colIndex); } } } // 2. 初始化数据集合 List<String> numberList = new ArrayList<>(); List<String> kpgzList = new ArrayList<>(); // 3. 遍历所有数据行(从第二行开始) int lastRowNum = xssfSheet.getLastRowNum(); for (int rowIndex = 1; rowIndex <= lastRowNum; rowIndex++) { Row dataRow = xssfSheet.getRow(rowIndex); if (dataRow == null) { continue; // 跳过空行 } // 动态获取Number列数据(如果表头存在) Integer numberColIndex = headerMap.get("Number"); if (numberColIndex != null) { Cell numberCell = dataRow.getCell(numberColIndex); if (numberCell != null) { String numberValue = getCellStringValue(numberCell).trim(); if (!numberValue.isEmpty()) { numberList.add(numberValue); log.info("number: {}. № {}", rowIndex, numberValue); } } } // 动态获取kpgz列数据(如果表头存在) Integer kpgzColIndex = headerMap.get("kpgz"); if (kpgzColIndex != null) { Cell kpgzCell = dataRow.getCell(kpgzColIndex); if (kpgzCell != null) { String kpgzValue = getCellStringValue(kpgzCell).trim(); if (!kpgzValue.isEmpty()) { kpgzList.add(kpgzValue); log.info("kpgz: {}. {}", rowIndex, kpgzValue); } } } } xssfWorkbook.close(); // 后续业务逻辑... return ResponseEntity.ok("解析完成"); } catch (IOException e) { log.error("Excel解析失败", e); return ResponseEntity.internalServerError().body("Excel解析异常"); } } // 工具方法:统一处理不同类型单元格的字符串转换 private String getCellStringValue(Cell cell) { if (cell == null) { return ""; } switch (cell.getCellType()) { case STRING: return cell.getStringCellValue(); case NUMERIC: if (DateUtil.isCellDateFormatted(cell)) { // 处理日期类型,可根据需求调整格式 return new SimpleDateFormat("yyyy-MM-dd").format(cell.getDateCellValue()); } else { // 处理数字类型,避免科学计数法 return BigDecimal.valueOf(cell.getNumericCellValue()).toPlainString(); } case BOOLEAN: return String.valueOf(cell.getBooleanCellValue()); case FORMULA: // 处理公式单元格,计算后获取值 try { return getCellStringValue(cell.getSheet().getWorkbook().getCreationHelper().createFormulaEvaluator().evaluateInCell(cell)); } catch (Exception e) { return cell.getCellFormula(); } default: return ""; } }
关键优化点
- 动态列映射:通过表头行构建
headerMap,后续完全根据列名获取数据,摆脱硬编码索引的限制 - 列缺失兼容:如果某列在表头中不存在,只会跳过该列的数据收集,不会抛出异常中断解析
- 单元格类型兼容:新增
getCellStringValue工具方法,统一处理字符串、数字、日期、公式等多种单元格类型,避免原代码中直接调用getStringCellValue导致的类型转换错误 - 代码精简:合并重复的遍历逻辑,只需要一次循环即可处理所有列的数据收集,后续新增列只需添加对应的数据收集逻辑
内容的提问来源于stack exchange,提问作者aleksandr1994
相关产品推荐
相关产品推荐

