Обработка большого файла xlsx
мне нужно автоматически поместить все строки в большой (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 ответов
попробуйте использовать API событий. См.API событий (только HSSF) и XSSF и SAX (Event API) в документации POI для деталей. Пара цитат с этой страницы:
Фип, ГСФ:
API событий новее, чем пользовательский API. Он предназначен для промежуточных разработчиков, которые готовы узнать немного о структурах API низкого уровня. Его относительно простой в использовании, но требует базового понимания частей файла Excel (или готовность учиться). Преимущество заключается в том, что вы можете читать XLS с относительно небольшим объемом памяти.
XSSF:
Если проблема с памятью, то для XSSF вы можете получить базовые XML-данные и обработать их самостоятельно. Это предназначено для промежуточных разработчиков, которые готовы узнать немного о низкоуровневой структуре .xlsx файлы, и кто счастлив обработки XML в java. Относительно простой в использовании, но требуется базовое понимание структуры файла. Преимущество заключается в том, что вы можете читать XLSX-файл с относительно небольшим объемом памяти.
для вывода, один из возможных подходов описан в блоге потоковое xlsx файлы. (В основном, используйте XSSF для создания XML-файла контейнера, а затем передайте фактическое содержимое в виде обычного текста в соответствующую xml-часть zip-архива xlsx.)
резкое улучшение использования памяти может быть сделано с помощью файла вместо потока. (Лучше использовать streaming API, но у Streaming API есть ограничения, см. http://poi.apache.org/spreadsheet/index.html)
Так вместо
Workbook workbook = WorkbookFactory.create(inputStream);
do
Workbook workbook = WorkbookFactory.create(new File("yourfile.xlsx"));
это согласно:http://poi.apache.org/spreadsheet/quick-guide.html#FileInputStream
файлы vs InputStreams
" при открытии книги либо a .xls HSSFWorkbook, или a .xlsx XSSFWorkbook, книга может быть загружена из файла или InputStream. Использование объекта File позволяет снизить потребление памяти, в то время как InputStream требует больше памяти, поскольку он должен буферизировать весь файл."
У меня была такая же проблема с гораздо меньшим количеством строк, но большими строками.
поскольку мне не нужно загружать данные, я обнаружил, что могу использовать SXSSF вместо XSSF.
у них похожие интерфейсы, что помогает, если у вас уже много кода. Но с SXSSF можно установить количество строк, которые вы держите загруженными.
вот ссылка. http://poi.apache.org/spreadsheet/how-to.html#sxssf
Если вы хотите автоматически подогнать или установить стили или записать все строки в большой (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");
Я использовал API событий для файла HSSF (.xls), и я обнаружил ужасное отсутствие документации о порядке записей.
вот пример, который я нашел, который будет обрабатывать очень большие файлы XLSX. Мое тестирование пока выглядит хорошо. Он способен обрабатывать очень большие файлы без проблем с памятью.
Если вы писать в XLSX я нашел улучшение, написав на разные листы одного и того же файла Excel. Вы также можете найти улучшение, написав в разные файлы Excel. Но сначала попробуйте написать на разных листах.
лучший пример для этого описан в следующем потоке переполнения стека: ошибка при чтении больших файлов Excel (xlsx) через Apache POI
фрагмент кода в основном ответе в этом разделе иллюстрирует обертки Apache POI вокруг синтаксического анализа SAX xml и то, как вы можете тривиально перебирать все листы, а затем каждую отдельную ячейку.
код устарел с текущей реализацией API POI Apache, поскольку api endRow() предоставляет текущий номер строки, которая закончила обработку.
с этим фрагментом кода должно быть тривиальным для вашего разбора большой XLSX-файл по ячейкам. Е. Г. для каждого листа; по каждой ячейке строки; строка мероприятие закончилось. Вы можете тривиально создать логику приложения, где в каждой строке вы создаете карту columneName в cellValue.
у меня была такая же проблема с 800,000 ячейками и символами 3M, где XSSF выделяет 1GB кучи!
я использовал 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, но в текстовом формате.