有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java Apache Poi无法读取工作表名称

关于通过Apache Poi读取Excel表格,我们遇到了一个奇怪的错误。我们使用的是5.0版

这段代码以前是有效的,但是现在它已经停止在我们所有的生产环境中工作。它在本地测试时仍然有效,因此很难调试

问题是我们返回了空的工作表名称,因此无法正确加载所需的工作表

try (XSSFWorkbook wb = new XSSFWorkbook(new FileInputStream(venueListFile))) {

        LOGGER.info("Found {} sheets", wb.getNumberOfSheets());

        // First setup venues
        Sheet venueSetUpSheet = wb.getSheet("Store Set Up");
        List<String> sheetNames = new ArrayList<>();
        for (Iterator<Sheet> it = wb.sheetIterator(); it.hasNext(); ) {
            sheetNames.add(it.next().getSheetName());
        }

        if (venueSetUpSheet == null) {

            LOGGER.warn("Sheet 'Store Set Up' not found, available sheets: '" + String.join("','", sheetNames) + "'");
        } else {
            LOGGER.info("Found sheets: " + String.join("','", sheetNames) + "'");

本地返回:

Found 5 sheets
Found sheets: Store Set Up','Store Open Hours','Staff Setup','TV Configurations','Sheet3'

在生产中,它返回的Excel文件相同:

Found 5 sheets
Sheet 'Store Set Up' not found, available sheets: 'null','null','null','null','null'

文件似乎已被读取,我们已测试服务器上上载的文件是否已损坏。有人知道Poi的已知问题会导致图纸名称为空吗


共 (1) 个答案

  1. # 1 楼答案

    XSSFSheet基于底层的org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet。而XSSFSheet.getSheetName只是返回org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet.getName

    如果返回null而不是名称,则可能使用了错误的类org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet。对于apache poi 5.0.0,它必须是CTSheet来自jar poi-ooxml-full-5.0.0.jarpoi-ooxml-lite-5.0.0.jar。它必须而不是来自ooxml-schemas-*.jar或任何poi-ooxml-schemas-*.jar像使用apache poi的较低版本一样。也许你生产环境中的某个库会运送或拉取ooxml-schemas-*.jar?这与apache poi 5.0.0是不相容的

    您可以询问ClassLoader一个特殊类(在您的例子中是org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet)在运行时来自哪里:

    ...
      ClassLoader classloader = org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet.class.getClassLoader();
      java.net.URL res = classloader.getResource("org/openxmlformats/schemas/spreadsheetml/x2006/main/CTSheet.class");
      String path = res.getPath();
      System.out.println("CTSheet came from " + path);
    ...
    

    如果这不是poi-ooxml-full-5.0.0.jarpoi-ooxml-lite-5.0.0.jar,那么您至少可以知道其他库也会提供或拉入该类

    如果这没有帮助,请同时检查org.apache.xmlbeans.*类来自哪里^org.openxmlformats.schemas.*类需要{}

    ...
      classloader = org.apache.xmlbeans.XmlObject.class.getClassLoader();
      res = classloader.getResource("org/apache/xmlbeans/XmlObject.class");
      path = res.getPath();
      System.out.println("XmlObject came from " + path);     
    ...
    

    使用apache poi 5.0.0,这个类必须来自xmlbeans-4.0.0.jar,它必须来自XmlBeans的任何其他版本

    如果所有这些都没有帮助,你也可以用同样的方法检查org.apache.poi.xssf.usermodel.XSSFSheet是否真的来自apache poi 5.0.0poi-ooxml-5.0.0.jar)。在您的生产环境中可能有多个apache poi版本。这也不受支持:

    ...
      classloader = org.apache.poi.xssf.usermodel.XSSFSheet.class.getClassLoader();
      res = classloader.getResource("org/apache/poi/xssf/usermodel/XSSFSheet.class");
      path = res.getPath();
      System.out.println("XSSFSheet came from " + path);     
    ...
    

    最后,您可以尝试从底层CTSheet对象获取工作表名称,如下所示:

    ...
      try (Workbook wb = WorkbookFactory.create(new FileInputStream("./test.xlsx"));) {
    
       List<String> sheetNames = new ArrayList<>();
       for (Iterator<Sheet> it = wb.sheetIterator(); it.hasNext(); ) {
        Sheet sheet = it.next();
        if (sheet instanceof org.apache.poi.xssf.usermodel.XSSFSheet) {
         java.lang.reflect.Field _sheet = org.apache.poi.xssf.usermodel.XSSFSheet.class.getDeclaredField("sheet");
         _sheet.setAccessible(true);
         org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet ctSheet = 
          (org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet)_sheet.get((org.apache.poi.xssf.usermodel.XSSFSheet)sheet);
         org.apache.xmlbeans.impl.values.TypeStore store = ((org.apache.xmlbeans.impl.values.XmlObjectBase)ctSheet).get_store();
         System.out.println(store);
         org.apache.xmlbeans.SimpleValue target = (org.apache.xmlbeans.SimpleValue)store.find_attribute_user(new javax.xml.namespace.QName("", "name"));
         System.out.println(target.getStringValue());
        }
        sheetNames.add(sheet.getSheetName());
       }
      
       System.out.println(sheetNames);
      }
    ...
    

    这样行吗?还是抛出例外?如果是,哪个

    当然,请检查所有的错误日志。由于Office Open XML将数据存储在XML中,使用Java解析XML时可能会出现问题?但这会引发异常,应该记录在某个地方