Как добавить столбцы в существующий большой файл Excel с помощью SXSSF Apache POI?

#java #excel #apache-poi #sxssf

Вопрос:

Я работаю с большим файлом Excel ( размером более 40 Мб , более 100 тыс. строк и 50 столбцов ). Я успешно читаю его, используя поток событий POI ( версия 3.10.1), а затем выполняю некоторые вычисления и сохраняю результат в списке.

Теперь я должен добавить этот список в виде столбца в тот же файл. В этой части я сталкиваюсь с проблемой.

Я попытался добиться этого, используя приведенный ниже код

 FileInputStream excelFile = new FileInputStream(new File(pathToFile));
Workbook workbook = new XSSFWorkbook(excelFile);
Sheet datatypeSheet = workbook.getSheetAt(0); // Get first sheet
Iterator<Row> iterator = datatypeSheet.iterator();

int i=0;
while (iterator.hasNext()) {  // Loop over each row
    Row currentRow = iterator.next();
    Cell cell = currentRow.createCell(currentRow.getLastCellNum());
    cell.setCellType(Cell.CELL_TYPE_STRING);
    if(currentRow.getRowNum() == 0)
        cell.setCellValue("OUTPUT-COLUMN"); // set column header for the new column
    else {
        cell.setCellValue(list.get(i));  // list contains the output to populate in new column
        i  ;
    }

}

FileOutputStream fos = new FileOutputStream(new File(pathToOutput));
workbook.write(fos);
fos.close();
 

Он отлично работает с файлами меньшего размера, но проблема в том, что у меня заканчивается память для файлов большего размера. Теперь я попытался изменить это и использовать SXSSF вместо XSFF, чтобы решить проблему с памятью (см. Код ниже). Но при тестировании даже для файлов меньшего размера я получаю выходной файл, такой же, как и входной файл.

 FileInputStream excelFile = new FileInputStream(new File(pathToFile));
XSSFWorkbook xwb = new XSSFWorkbook(inputStream);
inputStream.close();

SXSSFWorkbook wb = new SXSSFWorkbook(xwb,100); 
wb.setCompressTempFiles(true);
SXSSFSheet sh = (SXSSFSheet) wb.getSheetAt(0);
Iterator<Row> iterator = datatypeSheet.iterator();

int i=0;
while (iterator.hasNext()) {  // Loop over each row
    Row currentRow = iterator.next();
    Cell cell = currentRow.createCell(currentRow.getLastCellNum());
    cell.setCellType(Cell.CELL_TYPE_STRING);
    if(currentRow.getRowNum() == 0)
        cell.setCellValue("OUTPUT-COLUMN"); // set column header for the new column
    else {
        cell.setCellValue(list.get(i));  // list contains the output to populate in new column
        i  ;
    }

}

FileOutputStream fos = new FileOutputStream(new File(pathToOutput));
wb.write(fos);
fos.close();
 

Использование бд в моем случае не подходит, и я хочу избежать использования временной структуры данных для хранения данных для записи из-за ограничения памяти.

Есть ли способ записи в выходную книгу во время потоковой передачи ? Вот код, который я использую для чтения с помощью POI Streaming API

 private class ExcelData implements SheetContentsHandler {  

LinkedHashMap<Strin, String> rowMap;

    public void startRow(int rowNum) {
             
    }

    public void endRow(int rowNum) {
    // Process the row
    // Handle write to output workbook ??
    }

    public void cell(String cellReference, String formattedValue,
            XSSFComment comment) {
    // Save current row in rowMap ( column name => cell value )
    }

    public void headerFooter(String text, boolean isHeader, String tagName) 
    {
    }
}
 

Комментарии:

1. Сначала: Как вы пишете рабочие тетради? В ваших образцах кода нет кода, делающего это. Но этот SXSSF подход ведет в тупик. SXSSF предназначен только для добавления новых строк. Вы не можете редактировать все существующие строки SXSSF , так как эти строки больше не находятся в оперативной памяти. Вот как SXSSF экономится память. Q Если часть строк хранится в памяти в любой момент времени. Все остальные строки хранятся во временных файлах, а не в оперативной памяти.

2. @AxelRichter Я добавил код для написания рабочих книг. Я знал, что мы не можем изменять существующие ячейки с помощью SXSSF, но подумал, что мы могли бы добавить новую ячейку в конце каждой строки. В этом случае, что еще я могу попробовать.

3. Нет, как уже было сказано, доступна только часть новых созданных строк . Вам нужно, чтобы все данные находились в структуре за пределами SXSSF . Может быть, таблица базы данных? Затем вы можете записать их строку за строкой в SXSSFSheet . Или вы читаете каждую строку данных из книги, используя потоковые методы, и немедленно записываете эти данные строк, возможно, измененные и/или с использованием добавленных данных ячеек, в новую созданную SXSSFRow SXSSFSheet .

4. @AxelRichter Я уже читаю каждую строку с помощью потоковой передачи, реализуя SheetContentsHandler и переопределяя startRow (), cell( ) и endRow( ) . Я выполняю вывод calc. в методе endRow (). Я могу хранить каждую строку в списке или карте, но я стараюсь избегать использования структуры данных для хранения всех данных, так как это снова приведет к проблемам с памятью, если размер файла в будущем увеличится ( или это будет нормально ? ) О «немедленной записи данных строк» Должен ли я открывать , записывать и закрывать выходную книгу для каждой строки, которая передается как событие в методе cell() или endRow ()?

5. «избегайте использования структуры данных для хранения всех данных»: вот почему я предлагаю использовать базу данных. О «немедленной записи данных строк»: Нет, вам понадобятся две рабочие книги. Один для чтения с использованием потоковых методов, а другой SXSSFWorkbook -для записи.

Ответ №1:

Невозможно добавить столбец в существующую книгу с помощью POI SXSSF. Это позволяет добавлять только новые строки.

Единственное решение-прочитать существующую книгу и записать в новую книгу с добавленным столбцом.

Для достижения этой цели мы можем сохранить строки в структуре данных или базе данных в методе endrow (), а затем использовать сохраненные данные для написания новой книги.