Подтвердить что ты не робот

Обработка большого файла xlsx

Мне нужно автоподстроить все строки в большом (30k + rows) файле 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 не являются верхним пределом.

4b9b3361

Ответ 1

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

HSSF:

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

XSSF:

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

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

Ответ 2

Резкое улучшение использования памяти может быть выполнено с помощью файла вместо Stream. (Лучше использовать потоковый API, но Streaming 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

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

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

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

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

Ответ 4

Если вы хотите автоматически устанавливать или устанавливать стили или записывать все строки в большом (30k + rows) файле 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");

Ответ 5

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

Ответ 7

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

Ответ 8

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

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

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

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

Ответ 9

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

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

Питон script выглядит как

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, но в текстовом формате.

Ответ 10

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

fooobar.com/info/191623/...