Escrita / leitura com pouca memory com o Apache POI

Eu estou tentando escrever um arquivo XLSX muito grande (4M + células) e estou tendo alguns problemas de memory.

Não consigo usar o SXSSF, pois também preciso ler as células existentes no modelo.

Existe alguma coisa que eu possa fazer para reduzir a pegada de memory?
Talvez combinar leitura de streaming e gravação de streaming?

Para lidar com dados grandes com pouca memory, o melhor e acho que a única opção são as APIs do SXSSF. Se você precisar ler alguns dados das células existentes, presumo que você não precisa de todos os 4M + ao mesmo tempo. Nesse caso, com base nos seus requisitos de aplicativo, você pode manipular o tamanho da janela e manter na memory apenas a quantidade de dados necessária em um determinado momento. Você pode começar examinando o exemplo em: http://poi.apache.org/spreadsheet/how-to.html#sxssf

Algo como

SXSSFWorkbook wb = new SXSSFWorkbook(-1); // turn off auto-flushing and accumulate all rows in memory // manually control how rows are flushed to disk if(rownum % NOR == 0) { ((SXSSFSheet)sh).flushRows(NOR); // retain NOR last rows and flush all others 

Espero que isto ajude.

Eu usei o analisador SAX para processar events da apresentação do documento XML. Isto é

 import com.sun.org.apache.xerces.internal.parsers.SAXParser; import org.apache.poi.openxml4j.opc.PackageAccess; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.xssf.eventusermodel.XSSFReader; import org.apache.poi.xssf.model.SharedStringsTable; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.xssf.usermodel.XSSFRichTextString; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import java.io.BufferedInputStream; import java.io.InputStream; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; public class LowMemoryExcelFileReader { private String file; public LowMemoryExcelFileReader(String file) { this.file = file; } public List read() { try { return processFirstSheet(file); } catch (Exception e) { throw new RuntimeException(e); } } private List readSheet(Sheet sheet) { List res = new LinkedList<>(); Iterator rowIterator = sheet.rowIterator(); while (rowIterator.hasNext()) { Row row = rowIterator.next(); int cellsNumber = row.getLastCellNum(); String [] cellsValues = new String[cellsNumber]; Iterator cellIterator = row.cellIterator(); int cellIndex = 0; while (cellIterator.hasNext()) { Cell cell = cellIterator.next(); cellsValues[cellIndex++] = cell.getStringCellValue(); } res.add(cellsValues); } return res; } public String getFile() { return file; } public void setFile(String file) { this.file = file; } private List processFirstSheet(String filename) throws Exception { OPCPackage pkg = OPCPackage.open(filename, PackageAccess.READ); XSSFReader r = new XSSFReader(pkg); SharedStringsTable sst = r.getSharedStringsTable(); SheetHandler handler = new SheetHandler(sst); XMLReader parser = fetchSheetParser(handler); Iterator sheetIterator = r.getSheetsData(); if (!sheetIterator.hasNext()) { return Collections.emptyList(); } InputStream sheetInputStream = sheetIterator.next(); BufferedInputStream bisSheet = new BufferedInputStream(sheetInputStream); InputSource sheetSource = new InputSource(bisSheet); parser.parse(sheetSource); List res = handler.getRowCache(); bisSheet.close(); return res; } public XMLReader fetchSheetParser(ContentHandler handler) throws SAXException { XMLReader parser = new SAXParser(); parser.setContentHandler(handler); return parser; } /** * See org.xml.sax.helpers.DefaultHandler javadocs */ private static class SheetHandler extends DefaultHandler { private static final String ROW_EVENT = "row"; private static final String CELL_EVENT = "c"; private SharedStringsTable sst; private String lastContents; private boolean nextIsString; private List cellCache = new LinkedList<>(); private List rowCache = new LinkedList<>(); private SheetHandler(SharedStringsTable sst) { this.sst = sst; } public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { // c => cell if (CELL_EVENT.equals(name)) { String cellType = attributes.getValue("t"); if(cellType != null && cellType.equals("s")) { nextIsString = true; } else { nextIsString = false; } } else if (ROW_EVENT.equals(name)) { if (!cellCache.isEmpty()) { rowCache.add(cellCache.toArray(new String[cellCache.size()])); } cellCache.clear(); } // Clear contents cache lastContents = ""; } public void endElement(String uri, String localName, String name) throws SAXException { // Process the last contents as required. // Do now, as characters() may be called more than once if(nextIsString) { int idx = Integer.parseInt(lastContents); lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString(); nextIsString = false; } // v => contents of a cell // Output after we've seen the string contents if(name.equals("v")) { cellCache.add(lastContents); } } public void characters(char[] ch, int start, int length) throws SAXException { lastContents += new String(ch, start, length); } public List getRowCache() { return rowCache; } } }