2014-08-29 190 views
7

我想寫ResultSet轉Excel(* .xlsx)表使用Apache Poi。在Office ExcelResultSet轉Excel(* .xlsx)表使用Apache POI

無效的表對象錯誤

然而,即使它寫入Excel文件沒有任何錯誤,當我嘗試在Office Excel 2013中打開它,它顯示了一個錯誤,並刪除表格對象只給出純數據視圖。

Message while opening file

Message after removing errors

這裏是粗略的示例代碼using this example

public static void writeExcel(ResultSet rs, int sqliteRowCount, String dir) { 
    System.out.println("Writing Excel(*.xlsx) File..."); 
    XSSFWorkbook workbook = null; 
    try { 
     if (rs != null) { 
      // Get ResultSet MetaData 
      ResultSetMetaData rsmd = rs.getMetaData(); 
      // Number of columns 
      int numColumns = rsmd.getColumnCount(); 
      // Number of rows 
      // + 1 for headers 
      int numRows = sqliteRowCount + 1; 
      workbook = new XSSFWorkbook(); 

      // Create Excel Table 
      XSSFSheet sheet = workbook.createSheet("Text"); 
      XSSFTable table = sheet.createTable(); 
      table.setDisplayName("Test"); 
      CTTable cttable; 
      cttable = table.getCTTable(); 

      // Style configurations 
      CTTableStyleInfo style = cttable.addNewTableStyleInfo(); 
      style.setName("TableStyleMedium16"); 
      style.setShowColumnStripes(false); 
      style.setShowRowStripes(true); 

      // Set Table Span Area 
      AreaReference reference = new AreaReference(new CellReference(0, 0), new CellReference(numRows - 1, numColumns - 1)); 
      cttable.setRef(reference.formatAsString()); 
      cttable.setId(1); 
      cttable.setName("Test"); 
      cttable.setDisplayName("Test"); 
      cttable.setTotalsRowCount(numRows); 
      cttable.setTotalsRowShown(false); 

      // Create Columns 
      CTTableColumns columns = cttable.addNewTableColumns(); 
      columns.setCount(numColumns); 

      // Create Column, Row, Cell Objects 
      CTTableColumn column; 
      XSSFRow row; 

      // Add Header and Columns 
      XSSFRow headerRow = sheet.createRow(0); 
      for (int i = 0; i < numColumns; i++) { 
       column = columns.addNewTableColumn(); 
       column.setName("Column" + (i + 1)); 
       column.setId(i + 1); 
       headerRow.createCell(i).setCellValue(rsmd.getColumnLabel(i + 1)); 
      } 

      // Write each row from ResultSet 
      int rowNumber = 1; 
      while (rs.next()) { 
       row = sheet.createRow(rowNumber); 
       for (int y = 0; y < numColumns; y++) { 
        row.createCell(y).setCellValue(rs.getString(y + 1)); 
       } 
       rowNumber++; 
      } 

      // Set AutoFilter 
      CTAutoFilter fltr = CTAutoFilter.Factory.newInstance(); 
      fltr.setRef((new AreaReference(new CellReference(0, 0), new CellReference(numRows - 1, numColumns - 1))).formatAsString()); 
      cttable.setAutoFilter(fltr); 
      // sheet.setAutoFilter(CellRangeAddress.valueOf((new AreaReference(new CellReference(0, 0), new CellReference(numRows - 1, numColumns - 1))).formatAsString())); 
      // Freeze Pan 
      sheet.createFreezePane(0, 1, 0, 2); 
     } 
    } catch (SQLException ex) { 
     System.out.println("SQL Error while writing Excel file!"); 
    } finally { 
     try { 
     // Let's write the excel file now 
      if (workbook != null) { 
       String excelDir = dir + File.separator + "workbook.xlsx"; 
       try (final FileOutputStream out = new FileOutputStream(excelDir)) { 
        workbook.write(out); 
       } 
      } 
     } catch (IOException ex) { 
      System.out.println("IO Error while writing Excel summary file!"); 
     } 
    } 
} 

我知道什麼是錯我的代碼,但不能弄明白。 任何想法,爲什麼會發生這種情況,哪裏會出現我的代碼中的潛在錯誤。

更新1:在Excel檔案,如果表手動

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="1" name="Table1" displayName="Table1" ref="A1:B881" totalsRowShown="0"><autoFilter ref="A1:B881"/><tableColumns count="2"><tableColumn id="1" name="ID"/><tableColumn id="2" name="Name"/></tableColumns><tableStyleInfo name="TableStyleLight9" showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0"/></table> 

創建另外使用Apache POI

<?xml version="1.0" encoding="UTF-8"?> 
<table displayName="Test" ref="A1:B881" id="1" name="Test" totalsRowCount="881" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" totalsRowShown="0"><autoFilter ref="A1:B881"/><tableColumns count="2"><tableColumn name="ID" id="1"/><tableColumn name="Name" id="2"/><tableStyleInfo name="TableStyleMedium2" showColumnStripes="true" showRowStripes="true"/></table> 

表XML文件在Excel檔案,如果創建

表XML文件,如果我打開Excel歸檔文件,它沒有由Apache POI創建的主題文件夾,但它存在於一個創建中在Office Excel中手動。奇怪。

更新2: 樣品可執行代碼(使用NetBeans):

/* 
* To change this license header, choose License Headers in Project Properties. 
* To change this template file, choose Tools | Templates 
* and open the template in the editor. 
*/ 

package apachepoi_exceltest; 

    import java.io.File; 
    import java.io.FileOutputStream; 
    import java.io.IOException; 
    import java.util.HashMap; 
    import java.util.Map; 
    import org.apache.poi.ss.util.AreaReference; 
    import org.apache.poi.ss.util.CellRangeAddress; 
    import org.apache.poi.ss.util.CellReference; 
    import org.apache.poi.xssf.usermodel.XSSFRow; 
    import org.apache.poi.xssf.usermodel.XSSFSheet; 
    import org.apache.poi.xssf.usermodel.XSSFTable; 
    import org.apache.poi.xssf.usermodel.XSSFWorkbook; 
    import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTable; 
    import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn; 
    import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumns; 
    import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableStyleInfo; 

    /** 
    * 
    */ 
    public class ApachePOI_ExcelTest { 

     /** 
     * @param args the command line arguments 
     */ 
     public static void main(String[] args) { 

      String outputDir = "Your Local Directory Here"; 

      // TODO code application logic here 
      HashMap<String, String> dataMap = new HashMap<>(); 

      dataMap.put("ID 1", "Dummy Name 1"); 
      dataMap.put("ID 2", "Dummy Name 2"); 
      dataMap.put("ID 3", "Dummy Name 3"); 
      dataMap.put("ID 4", "Dummy Name 4"); 

      writeExcel(dataMap, outputDir); 

     } 

     private static void writeExcel(HashMap<String, String> dataMap, String outputDir) { 
      System.out.println("Writing Excel(*.xlsx) Summary File..."); 
      XSSFWorkbook workbook = null; 
      try { 

       // Number of columns 
       int numColumns = 2; // ID and Name 
       // Number of rows 
       int numRows = dataMap.size() + 1; // +1 for header 

       // Create Workbook 
       workbook = new XSSFWorkbook(); 

       // Create Excel Table 
       XSSFSheet sheet = workbook.createSheet("Summary"); 
       XSSFTable table = sheet.createTable(); 
       table.setDisplayName("Test"); 
       CTTable cttable; 
       cttable = table.getCTTable(); 

       // Style configurations 
       CTTableStyleInfo style = cttable.addNewTableStyleInfo(); 
       style.setName("TableStyleMedium16"); 
       style.setShowColumnStripes(false); 
       style.setShowRowStripes(true); 

       // Set Tabel Span Area 
       AreaReference reference = new AreaReference(new CellReference(0, 0), new CellReference(numRows - 1, numColumns - 1)); 
       cttable.setRef(reference.formatAsString()); 
       cttable.setId(1); 
       cttable.setName("Test"); 
       cttable.setDisplayName("Test"); 
       cttable.setTotalsRowCount(numRows); 
       cttable.setTotalsRowShown(false); 

       // Create Columns 
       CTTableColumns columns = cttable.addNewTableColumns(); 
       columns.setCount(numColumns); 

       // Create Column, Row, Cell Objects 
       CTTableColumn column; 
       XSSFRow row; 

       // Add ID Header 
       column = columns.addNewTableColumn(); 
       column.setName("Column" + (1)); 
       column.setId(1); 

       // Add Name Header 
       column = columns.addNewTableColumn(); 
       column.setName("Column" + (1)); 
       column.setId(1); 

       // Add Header Row 
       XSSFRow headerRow = sheet.createRow(0); 
       headerRow.createCell(0).setCellValue("ID"); 
       headerRow.createCell(1).setCellValue("Name"); 

       int rowNumber = 1; 
       for (Map.Entry<String, String> entry : dataMap.entrySet()) { 
        String id = entry.getKey(); 
        String name = entry.getValue(); 
        row = sheet.createRow(rowNumber); 
        row.createCell(0).setCellValue(id); 
        row.createCell(1).setCellValue(name); 
        rowNumber++; 
       } 

       // Set Filter (Below three lines code somehow not working in this example, so setting AutoFilter to WorkSheet) 
    //    CTAutoFilter fltr = CTAutoFilter.Factory.newInstance(); 
    //    fltr.setRef((new AreaReference(new CellReference(0, 0), new CellReference(numRows - 1, numColumns - 1))).formatAsString()); 
    //    cttable.setAutoFilter(fltr); 
       sheet.setAutoFilter(CellRangeAddress.valueOf((new AreaReference(new CellReference(0, 0), new CellReference(numRows - 1, numColumns - 1))).formatAsString())); 

       // Freeze First Row as header Row 
       sheet.createFreezePane(0, 1, 0, 2); 

      } catch (Exception ex) { 
       System.out.println("Error while writing Excel summary file!"); 
      } finally { 
       try { 
        // Lets write the Excel File Now 
        if (workbook != null) { 
         String excelDir = outputDir + File.separator + "workbook.xlsx"; 
         try (final FileOutputStream out = new FileOutputStream(excelDir)) { 
          workbook.write(out); 
         } 
        } 
       } catch (IOException ex) { 
        System.out.println("IO Error while writing Excel summary file!"); 
       } 
      } 
     } 

    } 

用於圖書館:

OOXML-架構 - 1.1.jar

POI 3.11 -beta2-20140822.jar

POI-OOXML-3.11-beta2-20140822.jar

的xmlbeans-2.6.0.jar

+0

您是否確定自己使用的是最新版本的Apache POI? (截至撰寫時爲3.11測試版) – Gagravarr 2014-08-29 16:27:59

+0

@Gagravarr:是的,我使用的是完全相同的版本。 – Indigo 2014-08-29 16:59:16

+0

嗯,討厭... Apache POI能夠讀取沒有錯誤生成的文件嗎?它能看到桌子嗎?那麼Open Office怎麼樣?它是否容忍文件+查看錶格,或者是否在Excel中投訴? – Gagravarr 2014-08-29 17:04:41

回答

0

我有同樣的問題。

深入挖掘,我發現對於XLSX包中的某些表XML數據,Excel在執行修復後將單個>更改爲&gt;。來自POI的XML很有意義(使用<>來包圍XML元素),所以我不知道爲什麼微軟選擇打破它。

如果您的情況相同,我不會太擔心。

如果你想看看你有沒有這個特殊的區別:

  1. 與POI
  2. 修復XLSX用Excel創建XLSX並保存到新的文件
  3. 開放與ZIP編輯這兩個文件(例如7Zip的)
  4. 查找XL /表/ table1.xml
  5. 同時導出XML文件(POI和Excel修補)
  6. DIFF文件
+0

當由Apache POI編寫時(使用應該這樣做的XML庫),所有內容都應該被轉義。你需要改變的是什麼? – Gagravarr 2014-08-29 23:50:34

+0

我調查的實例不涉及數據的更改。它是從Excel創建的XLSX文件讀取並保存到新文件操作(不執行其他操作)。 POI閱讀'>'轉換爲'>',但Excel認爲這是一個不好的改變。 – 2014-08-31 05:53:29

+0

你可以從前後發佈xml片段,因此我們可以看到它正在發生哪個xml元素? – Gagravarr 2014-08-31 10:00:06

0

您尚未正確創建表格。 檢查:

  • 您是否在cttable中創建了標題列?
  • 您是否通過cell.setCellValue創建了相同的標題列?
  • 在末尾刪除空第一集管柱(POI BUG)

    CTTable()getTableColumns()removeTableColumn(0)。。

將調試放入XSSFTable.class,方法updateHeaders()中。

如果你的表沒有正確創建,然後

XSSFRow row = sheet.getRow(headerRow); 

將在

/** 
* Synchronize table headers with cell values in the parent sheet. 
* Headers <em>must</em> be in sync, otherwise Excel will display a 
* "Found unreadable content" message on startup. 
*/ 
@SuppressWarnings("deprecation") 
public void updateHeaders(){ 
    XSSFSheet sheet = (XSSFSheet)getParent(); 
    CellReference ref = getStartCellReference(); 
    if(ref == null) return; 

    int headerRow = ref.getRow(); 
    int firstHeaderColumn = ref.getCol(); 
    XSSFRow row = sheet.getRow(headerRow); 

    if (row != null && row.getCTRow().validate()) { 
     int cellnum = firstHeaderColumn; 
     for (CTTableColumn col : getCTTable().getTableColumns().getTableColumnArray()) { 
      XSSFCell cell = row.getCell(cellnum); 
      if (cell != null) { 
       col.setName(cell.getStringCellValue()); 
      } 
      cellnum++; 
     } 
    } 
} 
3

有什麼問題你的代碼是一個單行的存在爲NULL。 「cttable.setTotalsRowCount(numRows);」 刪除它,一切都會工作。 如果有疑問,請比較Excel中手動創建的某些工作表的XML定義以及使用Apache POI創建的定義。