【问题标题】:ResultSet to Excel (*.xlsx) Table Using Apache POI使用 Apache POI 将结果集转换为 Excel (*.xlsx) 表
【发布时间】:2014-10-23 14:38:56
【问题描述】:

我正在尝试使用 Apache Poi 将 ResultSet 写入 Excel (*.xlsx) 表

Office Excel 中的无效表格对象错误

但是,即使它写入 Excel 文件时没有任何错误,但当我尝试在 Office Excel 2013 中打开它时,它会显示错误并删除表格对象以仅提供纯数据视图。

这里是粗略的示例代码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:

使用 Apache POI 创建的 Excel 存档中的表 XML 文件

<?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>

如果手动创建表格,则 Excel 存档中的表格 XML 文件

<?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>

此外,如果我打开 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-schemas-1.1.jar

poi-3.11-beta2-20140822.jar

poi-ooxml-3.11-beta2-20140822.jar

xmlbeans-2.6.0.jar

【问题讨论】:

  • 您确定您使用的是最新版本的 Apache POI 吗? (截至写作时为 3.11 beta 2)
  • @Gagravarr:是的,我使用的是完全相同的版本。
  • 嗯,讨厌... Apache POI 是否能够正确读取生成的文件?它可以看到桌子吗?那么 Open Office 怎么样 - 是容忍文件+查看表格,还是加入 Excel 抱怨?
  • @Gagravarr:哇哦,没试过。我在它上面:-)
  • 刚刚在 Ubuntu 上尝试了 LibreOffice,它运行良好,没有错误消息。我可以看到数据,但没有可见的表格样式。所以看来Table确实有问题。

标签: java excel apache-poi


【解决方案1】:

您的代码有什么问题是只有一行。 “cttable.setTotalsRowCount(numRows);” 删除它,一切都会工作。 如有疑问,请比较在 Excel 中手动创建的某些工作表的 XML 定义和使用 Apache POI 创建的定义

【讨论】:

    【解决方案2】:

    我遇到了同样的问题。

    深入挖掘,发现对于XLSX包中的一些表XML数据,修复后Excel是把单个&amp;gt;改成&amp;gt;。 POI 中的 XML 是有意义的(使用 &lt;&amp;gt; 包围 XML 元素)所以我不知道微软为什么选择破坏它。

    如果你的情况相同,我不会太担心。

    如果你想看看你是否有这个特殊的区别:

    1. 使用 POI 创建 XLSX
    2. 用 Excel 修复 XLSX 并保存到新文件
    3. 使用 ZIP 编辑器(例如 7Zip)打开这两个文件
    4. 找到 xl/tables/table1.xml
    5. 导出两个 XML 文件(POI 和 Excel 修复)
    6. 区分文件

    【讨论】:

    • 当由 Apache POI 编写时,所有内容都应该被转义(它使用应该这样做的 XML 库)。您必须更改的元素是什么?
    • 我调查的实例未涉及数据更改。这是从 Excel 创建的 XLSX 文件中读取并保存到新文件操作(未执行其他操作)。 POI 在&amp;gt; 中读取转换为&amp;gt;,但 Excel 认为这是一个糟糕的变化。
    • 你能把xml前后的sn-p贴出来,让我们看看是哪个xml元素发生的吗?
    • @EricS.:我不能简单地比较两个表来修复文件。因为 Excel 实际上是完全删除了这个表。所以它将表格视为无效对象。在删除表格之前,我仍然用 excel 更新了问题。
    【解决方案3】:

    您没有正确创建表。 检查:

    • 您是否在 cttable 中创建了标题列?
    • 您是否通过 cell.setCellValue 创建了相同的标题列?
    • 删除最后空的第一个标题列(POI BUG)

      CTTable().getTableColumns().removeTableColumn(0);

    把debug放到XSSFTable.class中,方法updateHeaders()。

    如果你的表没有正确创建,那么

    XSSFRow row = sheet.getRow(headerRow); 
    

    将在

    中为NULL
    /**
     * 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++;
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-01-10
      • 1970-01-01
      • 2018-05-19
      • 1970-01-01
      • 2014-03-27
      • 1970-01-01
      • 1970-01-01
      • 2023-01-26
      • 1970-01-01
      相关资源
      最近更新 更多