Обработка большого файла xlsx - PullRequest
34 голосов
/ 04 февраля 2011

Мне нужно автоматически подогнать все строки в большом (30k + строки) файле xlsx.

Следующий код через apache poi работает с небольшими файлами, но выходит с OutOfMemoryError на больших:

Workbook workbook = WorkbookFactory.create(inputStream);
Sheet sheet = workbook.getSheetAt(0);

for (Row row : sheet) {
    row.setHeight((short) -1);
}

workbook.write(outputStream);

Обновление: К сожалению, увеличение размера кучи не является опцией - OutOfMemoryError появляется в -Xmx1024m, а строки 30k не являются верхним пределом.

Ответы [ 10 ]

33 голосов
/ 18 февраля 2011

Попробуйте использовать API событий.Подробности см. В API событий (только HSSF) и XSSF и SAX (API событий) в документации по POI.Несколько цитат с этой страницы:

HSSF:

API события более новый, чем API пользователя.Он предназначен для разработчиков среднего уровня, которые хотят немного изучить структуры API низкого уровня.Он относительно прост в использовании, но требует базового понимания частей файла Excel (или готовности к обучению).Предоставленное преимущество заключается в том, что вы можете читать XLS с относительно небольшим объемом памяти.

XSSF:

Если объем памяти является проблемой, тогда для XSSF вы можете получить базовые данные XML и обработать их самостоятельно.Это предназначено для разработчиков среднего уровня, которые желают немного изучить низкоуровневую структуру файлов .xlsx и которым нравится обрабатывать XML в java.Он относительно прост в использовании, но требует базового понимания структуры файла.Предоставленное преимущество заключается в том, что вы можете прочитать файл XLSX с относительно небольшим объемом памяти.

Для вывода один из возможных подходов описан в сообщении блога Потоковая передача файлов xlsx .(В основном, используйте XSSF для генерации контейнерного XML-файла, а затем передайте реальное содержимое в виде простого текста в соответствующую XML-часть ZIP-архива xlsx.)

10 голосов
/ 09 июля 2013

Значительное улучшение использования памяти может быть достигнуто с помощью файла вместо потока. (Лучше использовать потоковый API, но у потокового API есть ограничения, см. http://poi.apache.org/spreadsheet/index.html)

Так что вместо

Workbook workbook = WorkbookFactory.create(inputStream);

сделать

Workbook workbook = WorkbookFactory.create(new File("yourfile.xlsx"));

Это согласно: http://poi.apache.org/spreadsheet/quick-guide.html#FileInputStream

Файлы против InputStreams

"При открытии рабочей книги, либо .xls HSSFWorkbook, либо .xlsx XSSFWorkbook, рабочую книгу можно загрузить из файла или из InputStream. Использование объекта File позволяет снизить потребление памяти, тогда как InputStream требует больше памяти как это должно буферизовать весь файл. "

3 голосов
/ 23 мая 2013

У меня была та же проблема с гораздо меньшим количеством строк, но большими строками.

Поскольку мне не нужно загружать свои данные, я обнаружил, что могу использовать SXSSF вместо XSSF.

Они имеют схожие интерфейсы, что помогает, если у вас уже написано много кода. Но с SXSSF можно установить количество сохраняемых строк.

Вот ссылка. http://poi.apache.org/spreadsheet/how-to.html#sxssf

2 голосов
/ 28 февраля 2015

Если вы хотите автоматически подгонять или устанавливать стили или записывать все строки в большой (30k + строки) файл xlsx, используйте SXSSFWorkbook. Вот пример кода, который поможет вам ...

SXSSFWorkbook wb = new SXSSFWorkbook();
            SXSSFSheet sheet = (SXSSFSheet) wb.createSheet("writetoexcel");
            Font font = wb.createFont();
                font.setBoldweight((short) 700);
                // Create Styles for sheet.
                XSSFCellStyle Style = (XSSFCellStyle) wb.createCellStyle();
                Style.setFillForegroundColor(new XSSFColor(java.awt.Color.LIGHT_GRAY));
                Style.setFillPattern(XSSFCellStyle.SOLID_FOREGROUND);
                Style.setFont(font);
                //iterating r number of rows
            for (int r=0;r < 30000; r++ )
            {
                Row row = sheet.createRow(r);
                //iterating c number of columns
                for (int c=0;c < 75; c++ )
                {
                    Cell cell = row.createCell(c);
                    cell.setCellValue("Hello"); 
                    cell.setCellStyle(Style);
                }
    }
            FileOutputStream fileOut = new FileOutputStream("E:" + File.separator + "NewTest.xlsx");
1 голос
/ 17 марта 2011

Я использовал Event API для файла HSSF (.xls) и обнаружил ужасную нехватку документации о порядке записей.

0 голосов
/ 07 июля 2017

Я использовал SAX-парсер для обработки структуры XML. Работает для файлов XLSX.

https://stackoverflow.com/a/44969009/4587961

0 голосов
/ 05 мая 2017

У меня была такая же проблема с 800 000 ячеек и 3M символов, где XSSF выделяет 1 ГБ кучи!

Я использовал Python с openpyxl и numpy для чтения файла xlsx (из кода Java) и сначалапреобразовать его в обычный текст.Затем я загрузил текстовый файл в Java.Может показаться, что это приводит к большим накладным расходам, но это действительно быстро.

Скрипт python выглядит как

import openpyxl as px
import numpy as np

# xlsx file is given through command line foo.xlsx
fname = sys.argv[1]
W = px.load_workbook(fname, read_only = True)
p = W.get_sheet_by_name(name = 'Sheet1')

a=[]
# number of rows and columns
m = p.max_row
n = p.max_column

for row in p.iter_rows():
    for k in row:
        a.append(k.value)

# convert list a to matrix (for example maxRows*maxColumns)
aa= np.resize(a, [m, n])

# output file is also given in the command line foo.txt
oname = sys.argv[2]
print (oname)
file = open(oname,"w")
mm = m-1
for i in range(mm):
    for j in range(n):
        file.write( "%s " %aa[i,j]  )
    file.write ("\n")

# to prevent extra newline in the text file
for j in range(n):
    file.write("%s " %aa[m-1,j])

file.close()

Затем в своем Java-коде я написал

try {
  // `pwd`\python_script  foo.xlsx  foo.txt
  String pythonScript =  System.getProperty("user.dir") + "\\exread.py ";
  String cmdline = "python " + pythonScript +
                    workingDirectoryPath + "\\" + fullFileName + " " + 
                    workingDirectoryPath + "\\" + shortFileName + ".txt";
  Process p = Runtime.getRuntime().exec(cmdline);
  int exitCode = p.waitFor();
  if (exitCode != 0) {
    throw new IOException("Python command exited with " + exitCode);
  }
} catch (IOException e) {
  System.out.println( e.getMessage() );
} catch (InterruptedException e) {
  ReadInfo.append(e.getMessage() );
}

После этого вы получите foo.txt, который похож на foo.xlsx, но в текстовом формате.

0 голосов
/ 18 января 2016

Лучший пример для этого описан в следующем потоке переполнения стека: Ошибка при чтении больших файлов Excel (xlsx) через Apache POI

Фрагмент кода в основном ответе в этом разделе иллюстрирует обертывание POI Apache вокруг синтаксического анализа SAX xml и то, как вы можете тривиально зацикливаться на всех листах и ​​затем на каждой отдельной ячейке.

Код устарел с текущей реализацией API Apache POI, так как endRow () api предоставляет номер текущей строки, завершившей обработку.

С этим фрагментом кода у вас должно быть тривиально проанализировать большой файл XLSX ячейка за ячейкой. Например. для каждого листа; для каждой строки строки; Строка закончила событие. Вы можете легко создать логику приложения, в которой в каждой строке вы создадите Map of columneName to cellValue.

0 голосов
/ 04 августа 2013

Если вы пишете в XLSX, я обнаружил улучшение, записав разные листы одного и того же файла Excel. Вы также можете найти улучшение, записав в разные файлы Excel. Но сначала попробуйте написать на разных листах.

0 голосов
/ 11 ноября 2012

Вот пример, который я нашел, который будет обрабатывать очень большие файлы XLSX.Мое тестирование пока выглядит хорошо.Он может обрабатывать очень большие файлы без проблем с памятью.

http://svn.apache.org/repos/asf/poi/trunk/src/examples/src/org/apache/poi/xssf/eventusermodel/XLSX2CSV.java

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...