#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 (), а затем использовать сохраненные данные для написания новой книги.