Es gibt einen Prozess zum Ausgeben einer Excel-Datei mithilfe des POI. Es ist eine Spezifikation, die bis zu 10000 Datenzeilen in 48 Spalten ausgibt. Als ich jedoch versuchte, 10000 Zeilen auszugeben, trat ein OutOfMemory-Fehler auf. Notieren Sie sich, wie Sie mit solchen Fällen umgehen sollen.
Das Bild der Quelle, bei der das Problem aufgetreten ist, lautet wie folgt.
Problemquelle
File file = new File(tempPath); //Temporärer Dateipfad Vorab eine Datei erstellt Verwenden Sie diese als Vorlage
XSSFWorkbook workbook = (XSSFWorkbook) WorkbookFactory.create(file);
XSSFSheet sheet = workbook.getSheetAt(0);
XSSFRow baseRow = sheet.getRow(sheet.getLastRowNum()); //Referenzzeile: Kopieren Sie den Stil jeder Zelle
int rowCnt = 1;
for (1 erfasste Daten: list) { //Es wird davon ausgegangen, dass die Liste im Voraus erhalten wurde und die Quelle nicht angegeben ist.
sheet.createRow(sheet.getLastRowNum() + 1);
XSSFRow newRow = sheet.getRow(sheet.getLastRowNum());
int cellCnt = 0;
XSSFCell originCell = null;
XSSFCell newCell = null;
XSSFCellStyle style = workbook.createCellStyle();
originCell = baseRow.getCell(cellCnt);
newCell = newRow.createCell(cellCnt++);
//Zellenstil kopieren
style.cloneStyleFrom(originCell.getCellStyle());
newCell.setCellStyle(style);
//Kopie des Zelltyps
newCell.setCellType(originCell.getCellType());
newCell.setCellValue(1 erfasste Daten.Wert);
//Wiederholen Sie die Einstellung für 14 Spalten
}
//Erstellungsdatum festlegen
sheet.getRow(0).getCell(2).setCellValue(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")));
//Basislinie löschen
sheet.shiftRows(baseRow.getRowNum() + 1, sheet.getLastRowNum(), -1);
//Schreiben Sie in den Ausgabestream und erhalten Sie das Byte-Array
ByteArrayOutputStream baos = new ByteArrayOutputStream();
workbook.write(baos);
excelData = baos.toByteArray();
baos.close();
workbook.close();
//Temporäre Datei löschen
file.delete();
//Fügen Sie das Byte-Array in die Antwort ein und laden Sie es herunter
Der Umriss des Prozesses ist wie folgt.
Beim Aufrufen von XSSFWorkbook.write () ist ein OutOfMemory-Fehler aufgetreten.
Wenn ich die Gegenmaßnahmenmethode gegoogelt habe, wird gesagt, dass die Verarbeitung einfacher ist, wenn SXSSF Workbook anstelle von XSSF Workbook verwendet wird. SXSSFWorkbook Ich habe auf die folgenden Dokumente verwiesen.
https://poi.apache.org/spreadsheet/#SXSSF+%28Since+POI+3.8+beta3%29
Es ist eine Erweiterung von XSSF. XSSF kann auf alle Zeilen zugreifen und alle API-Funktionen können verwendet werden. Da jedoch alle Zeileninformationen im Speicher erweitert werden, erhöht sich die Speicherbelegung. Andererseits scheint SXSSF die Speicherbelegung zu verringern, indem die Anzahl der zugänglichen Leitungen verringert wird. (Bitte weisen Sie darauf hin, wenn ein Missverständnis vorliegt)
Versuchen Sie, den vorherigen Code mithilfe der SXSSF-Arbeitsmappe zu ersetzen.
Ersetzen Sie die XSSF-Arbeitsmappe durch die SXSSF-Arbeitsmappe
File file = new File(tempPath); //Temporärer Dateipfad Vorab eine Datei erstellt Verwenden Sie diese als Vorlage
SXSSFWorkbook workbook = (SXSSFWorkbook) WorkbookFactory.create(file);
SXSSFSheet sheet = workbook.getSheetAt(0);
SXSSFRow baseRow = sheet.getRow(sheet.getLastRowNum()); //Referenzzeile: Kopieren Sie den Stil jeder Zelle
int rowCnt = 1;
for (1 erfasste Daten: list) { //Es wird davon ausgegangen, dass die Liste im Voraus erhalten wurde und die Quelle nicht angegeben ist.
sheet.createRow(sheet.getLastRowNum() + 1);
SXSSFRow newRow = sheet.getRow(sheet.getLastRowNum());
int cellCnt = 0;
SXSSFCell originCell = null;
SXSSFCell newCell = null;
CellStyle style = workbook.createCellStyle();
originCell = baseRow.getCell(cellCnt);
newCell = newRow.createCell(cellCnt++);
//Zellenstil kopieren
style.cloneStyleFrom(originCell.getCellStyle());
newCell.setCellStyle(style);
//Kopie des Zelltyps
newCell.setCellType(originCell.getCellType());
newCell.setCellValue(1 erfasste Daten.Wert);
//Wiederholen Sie die Einstellung für 14 Spalten
}
//Erstellungsdatum festlegen
sheet.getRow(0).getCell(2).setCellValue(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")));
//Basislinie löschen
sheet.shiftRows(baseRow.getRowNum() + 1, sheet.getLastRowNum(), -1);
//Schreiben Sie in den Ausgabestream und erhalten Sie das Byte-Array
ByteArrayOutputStream baos = new ByteArrayOutputStream();
workbook.write(baos);
excelData = baos.toByteArray();
baos.close();
workbook.close();
//Temporäre Datei löschen
file.delete();
//Fügen Sie das Byte-Array in die Antwort ein und laden Sie es herunter
Dies führt zu einem Fehler. .. ..
Fehlerinhalt
java.lang.ClassCastException: org.apache.poi.xssf.usermodel.XSSFWorkbook cannot be cast to org.apache.poi.xssf.streaming.SXSSFWorkbook
WorkbookFactory.create (Datei) unterstützt SXSSF Workbook nicht. Nach der Untersuchung kann beim Erstellen einer SXSSF-Arbeitsmappeninstanz eine XSSF-Arbeitsmappeninstanz als Argument angegeben werden.
Instanziierung der SXSSF-Arbeitsmappe
File file = new File(tempPath); //Temporärer Dateipfad Vorab eine Datei erstellt Verwenden Sie diese als Vorlage
XSSFWorkbook original = (XSSFWorkbook) WorkbookFactory.create(file);
SXSSFWorkbook workbook = new SXSSFWorkbook(original);
SXSSFSheet sheet = workbook.getSheetAt(0);
SXSSFRow baseRow = sheet.getRow(sheet.getLastRowNum()); //Referenzzeile: Kopieren Sie den Stil jeder Zelle
original.close(); //Wird in die SXSSF-Arbeitsmappe geladen(?)Schließen für
Ersetzen Sie die Instanziierung durch die obige und führen Sie sie erneut aus. Wenn die obige SXSSFSheet.getLastRowNum () ausgeführt wird, wird sie nullpo.
Fehlerinhalt 2
java.lang.NullPointerException
Da die Vorlagendatei durch Instanziieren der XSSF-Arbeitsmappe gelesen wird, stellte ich mir vor, dass auf die letzte Zeile der vorhandenen Zeile der Vorlagendatei zugegriffen werden kann, aber ein Fehler aufgetreten ist. Geben Sie daher die letzte Anzahl der vorhandenen Zeilen "6" an und führen Sie sie erneut aus. Der folgende Fehler wird auftreten.
Fehlerinhalt 3
java.lang.IllegalArgumentException: Attempting to write a row[6] in the range [0,6] that is already written to disk.
Anscheinend sind die vorhandenen Zeilen der von XSSF Workbook gelesenen Datei zum Zeitpunkt der Instanziierung von SXSSF Workbook nicht zugänglich.
Aufgrund der diesmaligen Implementierungsspezifikationen möchten wir jedoch den Zugriff auf vorhandene Leitungen ermöglichen. Aufgrund verschiedener Versuche und Irrtümer wurde festgestellt, dass auf die vorhandene Leitung in der folgenden Form zugegriffen werden kann. (Ich habe die richtige Methode nicht bestätigt, aber es kann getan werden)
Greifen Sie auf eine vorhandene Zeile in der Vorlagendatei zu
File file = new File(tempPath); //Temporärer Dateipfad Vorab eine Datei erstellt Verwenden Sie diese als Vorlage
XSSFWorkbook original = (XSSFWorkbook) WorkbookFactory.create(file);
XSSFSheet orgSheet = original.getSheetAt(0);
XSSFRow baseRow = sheet.getRow(sheet.getLastRowNum()); //Referenzzeile: Kopieren Sie den Stil jeder Zelle
SXSSFWorkbook workbook = new SXSSFWorkbook(original);
SXSSFSheet sheet = workbook.getSheetAt(0);
original.close(); //Wird in die SXSSF-Arbeitsmappe geladen(?)Schließen für
Bei der obigen Ausführung ist beim Aufrufen von SXSSFWorkbook.write () ein Fehler aufgetreten.
Fehlerinhalt 4
java.io.IOException: Zip bomb detected! The file would exceed the max. ratio of compressed file size to the size of the expanded data. This may indicate that the file is used to inflate memory usage and thus could pose a security risk. You can adjust this limit via ZipSecureFile.setMinInflateRatio() if you need to work with files which exceed this limit. Counter: 820224, cis.counter: 8192, ratio: 0.009987515605493134Limits: MIN_INFLATE_RATIO: 0.01
Nach der Untersuchung kann dies durch Aufrufen von ZipSecureFile.setMinInflateRatio () vermieden werden.
Die endgültige fertige Quelle mit dem Aufruf von ZipSecureFile.setMinInflateRatio () wird unten angezeigt.
Quelle abgeschlossen
File file = new File(tempPath); //Temporärer Dateipfad Vorab eine Datei erstellt Verwenden Sie diese als Vorlage
XSSFWorkbook original = (XSSFWorkbook) WorkbookFactory.create(file);
XSSFSheet orgSheet = original.getSheetAt(0);
SXSSFWorkbook workbook = new SXSSFWorkbook(original);
SXSSFSheet sheet = workbook.getSheetAt(0);
Row baseRow = orgSheet.getRow(orgSheet.getLastRowNum());
int rowCnt = 1;
int rowNum = 6;
boolean isFirst = true;
for (1 erfasste Daten: list) { //Es wird davon ausgegangen, dass die Liste im Voraus erhalten wurde und die Quelle nicht angegeben ist.
SXSSFRow newRow = sheet.createRow(rowNum++);
int cellCnt = 0;
XSSFCell originCell = null;
SXSSFCell newCell = null;
CellStyle style = workbook.createCellStyle();
if (isFirst) { //Führen Sie nur die erste Zeile aus
sheet.changeRowNum(newRow, 5);
rowNum = 6;
baseRow = newRow;
isFirst = false;
}
}
//Schreiben Sie in den Ausgabestream und erhalten Sie das Byte-Array
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipSecureFile.setMinInflateRatio(0.001); //Es scheint, dass sie die Komprimierungsrate und die Größe eines Eintrags überprüfen...
workbook.write(baos);
excelData = baos.toByteArray();
baos.close();
workbook.close();
original.close();
//Temporäre Datei löschen
file.delete();
//Fügen Sie das Byte-Array in die Antwort ein und laden Sie es herunter
Beim Ausführen tritt der OutOfMemory-Fehler nicht mehr auf.
Recommended Posts