Il existe un processus pour sortir un fichier Excel à l'aide de POI. C'est une spécification qui produit jusqu'à 10000 lignes de données dans 48 colonnes, mais lorsque j'ai essayé de sortir 10000 lignes, une erreur OutOfMemory s'est produite. Notez comment traiter de tels cas.
L'image de la source où le problème est survenu est la suivante.
Source du problème
File file = new File(tempPath); //Chemin du fichier temporaire Création d'un fichier à l'avance Utilisez-le comme modèle
XSSFWorkbook workbook = (XSSFWorkbook) WorkbookFactory.create(file);
XSSFSheet sheet = workbook.getSheetAt(0);
XSSFRow baseRow = sheet.getRow(sheet.getLastRowNum()); //Ligne de référence: copier le style de chaque cellule
int rowCnt = 1;
for (1 données acquises: list) { //La liste n'est pas précisée car elle est obtenue à l'avance.
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++);
//Copier le style de cellule
style.cloneStyleFrom(originCell.getCellStyle());
newCell.setCellStyle(style);
//Copie du type de cellule
newCell.setCellType(originCell.getCellType());
newCell.setCellValue(1 données acquises.valeur);
//Répéter le réglage pour 14 colonnes
}
//Définir la date de création
sheet.getRow(0).getCell(2).setCellValue(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")));
//Supprimer la ligne de base
sheet.shiftRows(baseRow.getRowNum() + 1, sheet.getLastRowNum(), -1);
//Écrire dans le flux de sortie et obtenir un tableau d'octets
ByteArrayOutputStream baos = new ByteArrayOutputStream();
workbook.write(baos);
excelData = baos.toByteArray();
baos.close();
workbook.close();
//Supprimer le fichier temporaire
file.delete();
//Mettez le tableau d'octets dans la réponse et téléchargez-le
Les grandes lignes du processus sont les suivantes.
Une erreur OutOfMemory s'est produite lors de l'appel de XSSFWorkbook.write ().
Lorsque j'ai recherché sur Google la méthode de contre-mesure, il est dit que le traitement sera plus léger si le classeur SXSSF est utilisé à la place du classeur XSSF. SXSSFWorkbook J'ai évoqué les documents suivants.
https://poi.apache.org/spreadsheet/#SXSSF+%28Since+POI+3.8+beta3%29
C'est une extension de XSSF. XSSF peut accéder à toutes les lignes et toutes les fonctions de l'API peuvent être utilisées, mais comme toutes les informations de ligne sont développées en mémoire, l'occupation de la mémoire augmente. D'autre part, SXSSF semble réduire l'occupation de la mémoire en réduisant le nombre de lignes accessibles. (Veuillez préciser s'il y a un malentendu)
Essayez de remplacer le code précédent à l'aide du classeur SXSSF.
Remplacer le classeur XSSF par le classeur SXSSF
File file = new File(tempPath); //Chemin du fichier temporaire Création d'un fichier à l'avance Utilisez-le comme modèle
SXSSFWorkbook workbook = (SXSSFWorkbook) WorkbookFactory.create(file);
SXSSFSheet sheet = workbook.getSheetAt(0);
SXSSFRow baseRow = sheet.getRow(sheet.getLastRowNum()); //Ligne de référence: copier le style de chaque cellule
int rowCnt = 1;
for (1 données acquises: list) { //La liste n'est pas précisée car elle est obtenue à l'avance.
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++);
//Copier le style de cellule
style.cloneStyleFrom(originCell.getCellStyle());
newCell.setCellStyle(style);
//Copie du type de cellule
newCell.setCellType(originCell.getCellType());
newCell.setCellValue(1 données acquises.valeur);
//Répéter le réglage pour 14 colonnes
}
//Définir la date de création
sheet.getRow(0).getCell(2).setCellValue(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")));
//Supprimer la ligne de base
sheet.shiftRows(baseRow.getRowNum() + 1, sheet.getLastRowNum(), -1);
//Écrire dans le flux de sortie et obtenir un tableau d'octets
ByteArrayOutputStream baos = new ByteArrayOutputStream();
workbook.write(baos);
excelData = baos.toByteArray();
baos.close();
workbook.close();
//Supprimer le fichier temporaire
file.delete();
//Mettez le tableau d'octets dans la réponse et téléchargez-le
Cela entraînera une erreur. .. ..
contenu de l'erreur
java.lang.ClassCastException: org.apache.poi.xssf.usermodel.XSSFWorkbook cannot be cast to org.apache.poi.xssf.streaming.SXSSFWorkbook
WorkbookFactory.create (fichier); ne prend pas en charge le classeur SXSSF. Après examen, il est possible de spécifier une instance de classeur XSSF comme argument lors de la création d'une instance de classeur SXSSF.
Instanciation du classeur SXSSF
File file = new File(tempPath); //Chemin du fichier temporaire Création d'un fichier à l'avance Utilisez-le comme modèle
XSSFWorkbook original = (XSSFWorkbook) WorkbookFactory.create(file);
SXSSFWorkbook workbook = new SXSSFWorkbook(original);
SXSSFSheet sheet = workbook.getSheetAt(0);
SXSSFRow baseRow = sheet.getRow(sheet.getLastRowNum()); //Ligne de référence: copier le style de chaque cellule
original.close(); //Chargé dans le classeur SXSSF(?)Fermer pour
Remplacez l'instanciation par ce qui précède et exécutez-la à nouveau. Lorsque le SXSSFSheet.getLastRowNum () ci-dessus est exécuté, il devient nullpo.
Contenu de l'erreur 2
java.lang.NullPointerException
Étant donné que le fichier modèle est lu en instanciant le classeur XSSF, j'ai imaginé que la dernière ligne de la ligne existante du fichier modèle était accessible, mais une erreur s'est produite. Par conséquent, spécifiez le dernier nombre de lignes existantes «6» et exécutez à nouveau. L'erreur suivante se produira.
Contenu de l'erreur 3
java.lang.IllegalArgumentException: Attempting to write a row[6] in the range [0,6] that is already written to disk.
Apparemment, les lignes existantes du fichier lu par XSSF Workbook sont inaccessibles au moment de l'instanciation de SXSSF Workbook.
Cependant, en raison des spécifications d'implémentation cette fois, nous souhaitons permettre l'accès aux lignes existantes. À la suite de divers essais et erreurs, il a été constaté que la ligne existante est accessible sous la forme suivante. (Je n'ai pas confirmé la bonne méthode, mais cela peut être fait)
Accéder à une ligne existante dans le fichier modèle
File file = new File(tempPath); //Chemin du fichier temporaire Création d'un fichier à l'avance Utilisez-le comme modèle
XSSFWorkbook original = (XSSFWorkbook) WorkbookFactory.create(file);
XSSFSheet orgSheet = original.getSheetAt(0);
XSSFRow baseRow = sheet.getRow(sheet.getLastRowNum()); //Ligne de référence: copier le style de chaque cellule
SXSSFWorkbook workbook = new SXSSFWorkbook(original);
SXSSFSheet sheet = workbook.getSheetAt(0);
original.close(); //Chargé dans le classeur SXSSF(?)Fermer pour
Dans l'exécution ci-dessus, une erreur s'est produite lors de l'appel de SXSSFWorkbook.write ().
Contenu de l'erreur 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
Après enquête, il est possible de l'éviter en appelant ZipSecureFile.setMinInflateRatio ().
La source de la version finale finale avec l'appel à ZipSecureFile.setMinInflateRatio () est indiquée ci-dessous.
Source terminée
File file = new File(tempPath); //Chemin du fichier temporaire Création d'un fichier à l'avance Utilisez-le comme modèle
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 données acquises: list) { //La liste n'est pas précisée car elle est obtenue à l'avance.
SXSSFRow newRow = sheet.createRow(rowNum++);
int cellCnt = 0;
XSSFCell originCell = null;
SXSSFCell newCell = null;
CellStyle style = workbook.createCellStyle();
if (isFirst) { //Exécuter uniquement la première ligne
sheet.changeRowNum(newRow, 5);
rowNum = 6;
baseRow = newRow;
isFirst = false;
}
}
//Écrire dans le flux de sortie et obtenir un tableau d'octets
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipSecureFile.setMinInflateRatio(0.001); //Il semble qu'ils vérifient le taux de compression et la taille d'une entrée...
workbook.write(baos);
excelData = baos.toByteArray();
baos.close();
workbook.close();
original.close();
//Supprimer le fichier temporaire
file.delete();
//Mettez le tableau d'octets dans la réponse et téléchargez-le
Lorsqu'elle est exécutée, l'erreur OutOfMemory ne se produit plus.
Recommended Posts