Apache POI xlsx Ошибка «Мы обнаружили проблему с некоторым содержимым в *.xlsx. Хотите, чтобы мы попытались восстановить его, насколько это возможно?» Ошибка из-за ошибки синтаксического анализа XML

Я создаю файл .xlsx с помощью Apache POI 5.2.5, отправляю массив байтов на сторону клиента и загружаю файл локально на клиентский компьютер.

При попытке открыть файл я получаю сообщение об ошибке «Мы обнаружили проблему с некоторым содержимым в *.xlsx. Хотите, чтобы мы попытались восстановить его, насколько это возможно». При нажатии «Да» файл открывается как в Windows, так и на Mac, и все данные присутствуют, но не в виде табличного объекта. Попытка открыть файл с помощью листов Google работает без проблем, а объект таблицы имеет границу.

Проблема сохранялась и тогда, когда я пытался сохранить ее непосредственно на сервере, поэтому я не думаю, что проблема на стороне клиента.

Код, генерирующий массив байтов:

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 или как ее избежать при создании файла на стороне сервера. Любая помощь будет оценена по достоинству.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
82
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Здесь несколько проблем.

Не используйте 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 = 0;
            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();
        }
    }
}

Спасибо. Как выяснилось. У меня возникла проблема с определенной областью таблицы. Я также столкнулся с проблемой с фильтром листов для таблицы, когда добавлял на страницу еще одну таблицу. Спасибо за пример!

Gal Mor 08.07.2024 08:24

Другие вопросы по теме