EXCEL解析之终极方法WorkbookFactory

本文详细介绍了一种使用Selenium进行Excel自动化测试的高效方法。通过对比传统处理方式与使用POI库WorkbookFactory类的方法,展示了如何简化Excel文件读取流程,无论文件格式为.xls还是.xlsx。此外,还提供了具体的代码示例帮助理解。
 EXCEL解析之终极方法WorkbookFactory

Selenium做自动化测试当然不能避免和Excel打交道。

由于Excel版本的关系,文件扩展名分xls和xlsx,

以往的经验都是使用HSSFWorkbook和XSSFWorkbook来分别处理。具体的方式就是先判断文件的类型,然后根据文件扩展名来选择方法。

大概处理方式如下:

String extention= getExtention(path);
             if (!EMPTY.equals(extention)) {
                 if (XLS.equals(extention)) {
                     return readXlsForAllSheets(path);
                 } else if (XLSX.equals(extention)) {
                     return readXlsxForAllSheets(path);
                 }
             } else {
                 System.out.println(path + " is not a excel file.");
             }

 再接着实现readXlsForAllSheets和readXlsxForAllSheets两个方法。

    public Object[][] readXlsxForAllSheets(String path) throws IOException{
         System.out.println(path);
         FileInputStream is = new FileInputStream(path);
        XSSFWorkbook xssfWorkbook = new XSSFWorkbook(is);
        System.out.println("There are totally "+xssfWorkbook.getNumberOfSheets()+" sheets in the workbook.");
         // Read the Sheet
        List<Object[]> records1=new ArrayList<Object[]>();
         for (int numSheet = 0; numSheet < xssfWorkbook.getNumberOfSheets(); numSheet++) {
             XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(numSheet);
             int rowCount=xssfSheet.getLastRowNum()-xssfSheet.getFirstRowNum();
             List<Object[]> records=new ArrayList<Object[]>();
             String[] separative={"This is sheet "+xssfWorkbook.getSheetName(numSheet)};
             records.add(separative);
             for(int rowNum =1;rowNum<rowCount+1; rowNum++){
                 XSSFRow xssfRow=xssfSheet.getRow(rowNum);
                 String fields[]=new String[xssfRow.getLastCellNum()];
                 for (int colNum=0;colNum<xssfRow.getLastCellNum();colNum++){
                    XSSFCell xssfCell=xssfRow.getCell(colNum);
                     fields[colNum]=this.getXssfCellValue(xssfCell);
                 }
                 records.add(fields);
             }
             records1.addAll(records);
             }
         Object[][] results=new Object[records1.size()][];
         for(int i=0;i<records1.size();i++){
             results[i]=records1.get(i);
         }
         if (xssfWorkbook!=null){xssfWorkbook.close();}
         return results;
         }

    public Object[][] readXlsForAllSheets(String path) throws IOException{
         System.out.println(PROCESSING + path);
         FileInputStream is = new FileInputStream(path);
        HSSFWorkbook hssfWorkbook = new HSSFWorkbook(is);
        System.out.println("There are totally "+hssfWorkbook.getNumberOfSheets()+" sheets in the workbook.");
         // Read the Sheet
        List<Object[]> records1=new ArrayList<Object[]>();
         for (int numSheet = 0; numSheet < hssfWorkbook.getNumberOfSheets(); numSheet++) {
             HSSFSheet hssfSheet = hssfWorkbook.getSheetAt(numSheet);
             int rowCount=hssfSheet.getLastRowNum()-hssfSheet.getFirstRowNum();
             List<Object[]> records=new ArrayList<Object[]>();
             String[] separative={"This is sheet "+hssfWorkbook.getSheetName(numSheet)};
             records.add(separative);
             for(int rowNum =1;rowNum<rowCount+1; rowNum++){
                 HSSFRow xssfRow=hssfSheet.getRow(rowNum);
                 String fields[]=new String[xssfRow.getLastCellNum()];
                 for (int colNum=0;colNum<xssfRow.getLastCellNum();colNum++){
                    HSSFCell xssfCell=xssfRow.getCell(colNum);
                     fields[colNum]=this.getHssfCellValue(xssfCell);
                 }
                 records.add(fields);
             }
             records1.addAll(records);
             }
         Object[][] results=new Object[records1.size()][];
         for(int i=0;i<records1.size();i++){
             results[i]=records1.get(i);
         }
         if (hssfWorkbook!=null){hssfWorkbook.close();}
         return results;
         }

 再实现上两个方法中调用的getXssfCellValue和getHssfCellValue方法。

 private String getXssfCellValue(XSSFCell xssfCell) {
        String cellvalue="";
        DataFormatter formatter = new DataFormatter();
        if (null != xssfCell) {  
            switch (xssfCell.getCellType()) {  
            case XSSFCell.CELL_TYPE_NUMERIC: // 数字  
                if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(xssfCell)) {
                  cellvalue = formatter.formatCellValue(xssfCell);
              } else {
                  double value = xssfCell.getNumericCellValue();
                  int intValue = (int) value;
                  cellvalue = value - intValue == 0 ? String.valueOf(intValue) : String.valueOf(value);
              }
                break;
            case XSSFCell.CELL_TYPE_STRING: // 字符串  
                cellvalue=xssfCell.getStringCellValue();
                break;  
            case XSSFCell.CELL_TYPE_BOOLEAN: // Boolean  
                cellvalue=String.valueOf(xssfCell.getBooleanCellValue());
                break;  
            case XSSFCell.CELL_TYPE_FORMULA: // 公式  
                cellvalue=String.valueOf(xssfCell.getCellFormula());  
                break;  
            case XSSFCell.CELL_TYPE_BLANK: // 空值  
                cellvalue="";  
                break;  
            case XSSFCell.CELL_TYPE_ERROR: // 故障  
                cellvalue="";  
                break;  
            default:  
                cellvalue="UNKNOWN TYPE";  
                break;  
            }  
        } else {  
            System.out.print("-");  
        }
        return cellvalue.trim();
    }

 
private String getHssfCellValue(HSSFCell hssfCell) {
        String cellvalue="";
        DataFormatter formatter = new DataFormatter();
        if (null != hssfCell) {  
            switch (hssfCell.getCellType()) {  
            case HSSFCell.CELL_TYPE_NUMERIC: // 数字  
                if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(hssfCell)) {
                    cellvalue = formatter.formatCellValue(hssfCell);
                } else {
                    double value = hssfCell.getNumericCellValue();
                    int intValue = (int) value;
                    cellvalue = value - intValue == 0 ? String.valueOf(intValue) : String.valueOf(value);
                }
                break;
            case HSSFCell.CELL_TYPE_STRING: // 字符串  
                cellvalue=hssfCell.getStringCellValue();
                break;  
            case HSSFCell.CELL_TYPE_BOOLEAN: // Boolean  
                cellvalue=String.valueOf(hssfCell.getBooleanCellValue());
                break;  
            case HSSFCell.CELL_TYPE_FORMULA: // 公式  
                cellvalue=String.valueOf(hssfCell.getCellFormula());  
                break;  
            case HSSFCell.CELL_TYPE_BLANK: // 空值  
                cellvalue="";  
                break;  
            case HSSFCell.CELL_TYPE_ERROR: // 故障  
                cellvalue="";  
                break;  
            default:  
                cellvalue="UNKNOWN TYPE";  
                break;  
            }  
        } else {  
            System.out.print("-");  
        }
        return cellvalue.trim();
    }

 最终整个解析Excel文件的功能才算完成,我们需要实现4个方法readXlsForAllSheets和readXlsxForAllSheets,getXssfCellValue和getHssfCellValue,那么有没有更加简单实用的方法呢?

下面要介绍的是POI jar包提供的WorkbookFactory类。需要加载poi-ooxm-3.15.jar到build path。

只需要两行就可以实例化workbook,而不用管它是xls还是xlsx。
1
2
    
inStream = new FileInputStream(new File(filePath));
Workbook workBook = WorkbookFactory.create(inStream);

 后续可以直接操作sheet,Row,Cell,也不用管文件类型。

目前还没有发现这种方法的缺点。
<think>嗯,用户现在关心两个问题:Xalan包的作用和POI升级后的"cannotcloseio"错误解决方案。之前已经详细讨论过POI的修复方案,用户可能在实际操作中遇到了新问题,或是想确认Xalan是否与错误有关。用户提到要参考站内引用,但当前对话中关于Xalan的内容不多。需要明确:Xalan是Apache的XSLT处理器,主要用于XML转换和XPath查询,而POI是处理Office文档的库,两者在功能上并不直接相关。POI的IO错误通常源于自身的资源管理机制变更,不太可能由Xalan引起。关于POI5.4.0的报错,上次已给出完整解决方案(基于try-with-resources和依赖管理)。用户重复提问可能意味着:1)之前的方案未完全解决问题;2)用户环境存在特殊性。建议深入排查以下可能性:-是否在处理超大Excel文件?OPCPackage.open()可能需要显式close-是否混用了POI的Workbook.write()和OPCPackage.flush()?二者同时调用会导致冲突-检查是否第三方库(如Log4j)拦截了close()调用,需更新相关依赖对于Xalan的作用补充:它本质是XML样式表引擎,核心场景是:1.将XML通过XSLT转换成HTML/PDF等格式2.执行XPath表达式提取XML数据3.对XML文档进行高级过滤和重组典型代码示例:```javaTransformerFactorytf=TransformerFactory.newInstance();Transformert=tf.newTransformer(newStreamSource("style.xsl"));t.transform(newStreamSource("data.xml"),newStreamResult(System.out));```如果用户项目中确实需要Xalan(如生成自定义报表),要注意其2.7.2版本与Java9+存在兼容性问题,可改用Xsltc编译器优化性能。最后强调:POI的错误解决方案依然有效,但若用户遇到新现象,建议提供完整异常栈——特别关注"Causedby"部分,可能有被忽略的嵌套异常(如ZIP流关闭失败)。</think> ### Xalan包的功能与用途 Xalan是Apache基金会开源的**XSLT处理器**实现,主要作用是将XML文档转换为其他格式(如HTML、PDF或文本)或执行XPath查询。其核心能力包括: 1. **XSLT转换(核心功能)** 通过XSL样式表将XML转换为任意文本格式: ```java TransformerFactory tf = TransformerFactory.newInstance(); Transformer t = tf.newTransformer(new StreamSource("style.xsl")); // 加载样式表 t.transform(new StreamSource("data.xml"), new StreamResult(System.out)); // 执行转换 ``` 2. **XPath表达式解析** 支持通过XPath定位XML节点: ```java XPath xpath = XPathFactory.newInstance().newXPath(); NodeList nodes = (NodeList) xpath.evaluate("//book[price>10]", doc, XPathConstants.NODESET); ``` 3. **依赖关系** 通常需要搭配以下包使用: ``` xalan-2.7.2.jar // 核心转换器 serializer-2.7.2.jar // 输出格式化 xml-apis-1.4.01.jar // XML规范支持 ``` > 📌 **与POI的关系**:Xalan并非POI的直接依赖,但当POI处理包含XML组件的文档(如OOXML格式的.xlsx)时,Java运行时可能自动加载Xalan解析文档结构[^1]。 --- ### POI 5.4.0升级后"can not close io"错误解决方案 #### 错误本质:资源管理冲突 POI 5.4.0强化了对 `InputStream`/`OutputStream`的生命周期管理。该错误通常发生在: 1. 手动调用 `close()` 后POI再次尝试关闭流 2. 多个线程共享未同步的流对象 #### 修复方案(分场景) **场景1:读取文件时的修复** ```java try (InputStream is = Files.newInputStream(Paths.get("data.xlsx")); // ✅ try-with-resources确保自动关闭 Workbook wb = WorkbookFactory.create(is)) { // POI 5.4.0+推荐方式 // 处理工作簿... } // 此处JVM自动关闭is和wb ``` **场景2:写入文件的修复** ```java try (Workbook wb = new XSSFWorkbook(); // ✅ 创建可自动关闭的工作簿 OutputStream os = new FileOutputStream("output.xlsx")) { wb.write(os); } // 自动关闭资源链 ``` #### 特殊场景处理 **当使用 `OPCPackage` 时(大文件处理)** ```java try (OPCPackage pkg = OPCPackage.open("large_file.xlsx")) { // ✅ 显式资源声明 XSSFWorkbook wb = new XSSFWorkbook(pkg); // ...操作工作簿 } // 自动关闭pkg底层流 ``` #### 依赖冲突排查 若异常持续出现,检查是否旧版本依赖干扰: ```xml <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.4.0</version> <exclusions> <!-- 排除潜在冲突 --> <exclusion> <groupId>stax</groupId> <artifactId>stax-api</artifactId> </exclusion> </exclusions> </dependency> ``` > ⚠️ 关键:POI 5.4.0+ **禁止**在代码中显式调用 `workbook.close()` 或 `stream.close()`,必须依赖JVM自动管理[^2]。 --- ### 技术原理补充 1. **POI资源管理机制** POI 5.4.0使用 `AutoCloseable` 接口重构资源管理,其内部关闭逻辑遵循: $$ \text{Workbook.close()} \rightarrow \text{自动关闭所有关联的PackagePart流} $$ 2. **与Xalan的潜在交互** 当处理含自定义XML的Excel时,POI可能触发Java内置的Xalan调用。此时若存在: ```mermaid graph LR POI[POI的OPCPackage] --> XML[Java的XML解析器] XML --> Xalan[Xalan处理器] ``` 需确保**整个调用链**的资源管理一致性。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值