Apache POI xlsx 由于 XML 解析错误,出现“我们发现 *.xlsx 中的某些内容存在问题。您希望我们尽力恢复它吗”错误

问题描述 投票:0回答:1

我正在使用 Apache POI 5.2.5 生成 .xlsx 文件,将字节数组发送到客户端并在客户端计算机上本地下载该文件。

尝试打开文件时,我收到“我们发现 *.xlsx 中的某些内容存在问题。您是否希望我们尝试尽可能地恢复它”错误。单击“是”,文件将在 Windows 和 Mac 上打开,并且所有数据都存在,但不是作为表对象。尝试使用谷歌表格打开文件没有问题,并且表格对象有边框。

当我尝试将其直接保存在服务器计算机上时,问题仍然存在,所以我认为问题不在客户端。

生成字节数组的代码:

private byte[] generateExcelBytes(List<DATA_OBJECT> data) {
    try (XSSFWorkbook workbook = new XSSFWorkbook()) {
        XSSFSheet sheet = workbook.createSheet("Data");

        CellStyle headerStyle = workbook.createCellStyle();
        Font headerFont = workbook.createFont();
        headerFont.setBold(true);
        headerFont.setFontHeightInPoints((short) 14);
        headerStyle.setFont(headerFont);

        CellStyle dataStyle = workbook.createCellStyle();
        Font dataFont = workbook.createFont();
        dataFont.setFontHeightInPoints((short) 12);
        dataStyle.setFont(dataFont);

        createTable(sheet, 0, 0, headerStyle, dataStyle, data);

        // Write the workbook content to a ByteArrayOutputStream
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        workbook.write(outputStream);
        return outputStream.toByteArray();
    } catch (IOException e) {
        logger.error("Exception when creating xlsx file for account {} CDI status tables", account.getId(), e);
    }
    return null;
}

private void createTable(XSSFSheet sheet, int startRow, int startCol, CellStyle headerStyle, CellStyle dataStyle, List<DATA_OBJECT> data) {
    // Create sample data (replace with your actual data generation logic)
    Row headerRow = sheet.createRow(startRow);

    // for each header
    createCell(headerRow, startCol + X, HEADER_NAME, headerStyle);

    // Create data rows
    for (int i = 0; i < data.size(); i++) {
        DATA_OBJECT data_object = data.get(i);
        Row dataRow = sheet.createRow(startRow + i + 1);
        // for each data object
        createCell(dataRow, startCol, data_object.get_relevant_value, dataStyle);
    }

    int endRow = startRow + baseHoldings.size();
    int endCol = startCol + NUM_OF_COLUMNS;

    XSSFAutoFilter autoFilter = sheet.setAutoFilter(new CellRangeAddress(startRow, endRow, startCol, endCol));
    for (int i = startCol; i <= startCol + NUM_OF_COLUMNS; i++) {
        sheet.autoSizeColumn(i);
        sheet.setColumnWidth(i, sheet.getColumnWidth(i) + 1000);
    }

    // Create Excel table
    XSSFTable table = sheet.createTable(null);

    CTTable cttable = table.getCTTable();
    cttable.setDisplayName("Data Table");
    cttable.setId(1);
    cttable.setName("Data_Table");
    cttable.setRef(new CellRangeAddress(startRow, endRow, startCol, endCol).formatAsString());
}

private void createCell(Row row, int columnIndex, String value, CellStyle style) {
    Cell cell = row.createCell(columnIndex);
    cell.setCellValue(value);
    cell.setCellStyle(style);
}

在 Mac 上打开文件,我发现了一个实际错误:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<recoveryLog xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
    <logFileName>Repair Result to <FILE_NAME>0.xml</logFileName>
    <summary>Errors were detected in file '<FILE_PATH>.xlsx'</summary>
    <removedParts summary="Following is a list of removed parts:">
        <removedPart>Removed Part: /xl/tables/table1.xml part with XML error.  (Table) Xml parsing error Line 2, column 70.</removedPart>
    </removedParts>
</recoveryLog>

/xl/tables/table1.xml 文件内容:

<?xml version="1.0" encoding="UTF-8"?>
<table id="1" displayName="Data Table" name="Data_Table" ref="A1:H3" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"/>

我不知道是什么原因导致XML解析错误,或者在服务器端生成文件时如何避免它。 任何帮助将不胜感激。

java excel apache-poi
1个回答
0
投票

这里有多个问题。

请勿在

areaReference
构造函数中使用
null
==
XSSFTable
。相反,将表的数据范围定义为
AreaReference

避免使用

CTTable
。 Apache POI 5.2.5 提供了
XSSFTable
中的大部分必需功能。在您的情况下,您忘记创建需要与工作表单元格相对应的表列。当
XSSFTable table = sheet.createTable(areaReference)
不是
areaReference
而是正确的
null
时,这是由
AreaReference
正确完成的。

显示名称不得包含空格。

并且表格不能有工作表过滤器。表使用自己的过滤器。不幸的是到目前为止

XSSFTable
还没有提供。因此必须使用
CTTable
table.getCTTable().addNewAutoFilter().setRef(areaReference.formatAsString());

不太确定你的

List<DATA_OBJECT> data
,所以我不太确定你计算的
endRow
endCol
是否正确。当然,表格不能从
startRow
开始,因为表头位于表格之外,不是吗?

使用大部分方法的完整示例,但为了简单起见,使用了

Object[][] data
和结果文件:

import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFTable;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFAutoFilter;

public class CreateTableExample {
    
    static void createTable(XSSFSheet sheet, int startRow, int startCol, CellStyle headerStyle, CellStyle dataStyle, Object[][] data) {
        
        Row headerRow = sheet.createRow(startRow);

        // for each header
        createCell(headerRow, startCol, "HEADER_NAME", headerStyle);
        int headerRows = 1; // how many rows outside table?
        
        int endRow = startRow + headerRows + data.length - 1;
        int endCol = startCol + data[0].length - 1;
        
        // Merge cells for Header
        sheet.addMergedRegion(new CellRangeAddress(startRow, startRow, startCol, endCol));

        // Create data rows
        for (int i = 0; i < data.length; i++) {
            Object[] data_object = data[i];
            Row dataRow = sheet.createRow(startRow + headerRows + i);
            int c = startCol;
            for (Object value : data_object) {
                createCell(dataRow, startCol + c++, value, dataStyle);
            }
        }

        //XSSFAutoFilter autoFilter = sheet.setAutoFilter(new CellRangeAddress(startRow + headerRows, endRow, startCol, endCol)); // No sheet filter for Table!
        for (int i = startCol; i <= endCol; i++) {
            sheet.autoSizeColumn(i);
            sheet.setColumnWidth(i, sheet.getColumnWidth(i) + 1000);
        }

        // Define the data range for the table
        AreaReference areaReference = new AreaReference(new CellReference(startRow + headerRows, startCol), new CellReference(endRow, endCol), SpreadsheetVersion.EXCEL2007);

        // Create Excel table
        //XSSFTable table = sheet.createTable(null); // do not use areaReference == null!
        XSSFTable table = sheet.createTable(areaReference);
        // Set the table style
        table.getCTTable().addNewTableStyleInfo();
        table.getCTTable().getTableStyleInfo().setName("TableStyleLight12");
            
        table.setName("Data Table");
        table.setDisplayName("Data_Table"); // display name must not contain spaces!
        table.getCTTable().addNewAutoFilter().setRef(areaReference.formatAsString()); // set AutoFilter in table
    }

    static void createCell(Row row, int columnIndex, Object value, CellStyle style) {
        Cell cell = row.createCell(columnIndex);
        if (value instanceof String) {
            cell.setCellValue((String)value);
        } else if (value instanceof Number) {
            cell.setCellValue(((Number)value).doubleValue());           
        }
        cell.setCellStyle(style);
    }   

    public static void main(String[] args) {
        
        // Create sample data (replace with your actual data generation logic)
        Object[][] data = new Object[][] {
            new Object[] {"Name", "Value", "Class", "Amount"},
            new Object[] {"Name 1", 123, "Class 1", 123.45},
            new Object[] {"Name 2", 456, "Class 2", 456.78},
            new Object[] {"Name 3", 789, "Class 3", 789.01},
            new Object[] {"Name 4", 123, "Class 4", 123.45},
            new Object[] {"Name 5", 456, "Class 5", 456.78},
            //...
        };
        
        try (Workbook workbook = new XSSFWorkbook()) {
            Sheet sheet = workbook.createSheet("Sheet1");

            CellStyle headerStyle = workbook.createCellStyle();
            Font headerFont = workbook.createFont();
            headerFont.setBold(true);
            headerFont.setFontHeightInPoints((short) 14);
            headerStyle.setFont(headerFont);

            CellStyle dataStyle = workbook.createCellStyle();
            Font dataFont = workbook.createFont();
            dataFont.setFontHeightInPoints((short) 12);
            dataStyle.setFont(dataFont);

            createTable((XSSFSheet)sheet, 0, 0, headerStyle, dataStyle, data);
            
            // Save the workbook
            try (FileOutputStream fileOut = new FileOutputStream("./workbook_with_table.xlsx")) {
                workbook.write(fileOut);
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.