web-dev-qa-db-fra.com

Traitement de gros fichiers xlsx

Je dois ajuster automatiquement toutes les lignes dans un grand fichier xlsx (30k + lignes).

Le code suivant via Apache poi fonctionne sur les petits fichiers, mais sort avec OutOfMemoryError sur les gros:

Workbook workbook = WorkbookFactory.create(inputStream);
Sheet sheet = workbook.getSheetAt(0);

for (Row row : sheet) {
    row.setHeight((short) -1);
}

workbook.write(outputStream);

Mise à jour: Malheureusement, l’augmentation de la taille du segment de mémoire n’est pas une option. OutOfMemoryError apparaît à -Xmx1024m et 30 000 lignes ne constituent pas une limite supérieure.

33
miah

Essayez d'utiliser l'API de l'événement. Voir API d'événement (HSSF uniquement) et XSSF et SAX (API d'événement) dans la documentation de POI pour plus de détails. Quelques citations de cette page:

HSSF:

L'API d'événement est plus récente que l'API utilisateur. Il est destiné aux développeurs intermédiaires qui souhaitent apprendre un peu les structures d'API de bas niveau. Il est relativement simple à utiliser, mais nécessite une compréhension de base des parties d’un fichier Excel (ou la volonté d’apprendre). L'avantage fourni est que vous pouvez lire un fichier XLS avec une empreinte mémoire relativement faible. 

XSSF:

Si l'encombrement de la mémoire est un problème, pour XSSF, vous pouvez accéder aux données XML sous-jacentes et les traiter vous-même. Ceci est destiné aux développeurs intermédiaires qui souhaitent apprendre un peu la structure de bas niveau des fichiers .xlsx et qui sont heureux de traiter XML en Java. C'est relativement simple à utiliser, mais nécessite une compréhension de base de la structure du fichier. L'avantage fourni est que vous pouvez lire un fichier XLSX avec une empreinte mémoire relativement faible.

Pour la sortie, une approche possible est décrite dans l’article de blog Streaming xlsx files . (Fondamentalement, utilisez XSSF pour générer un fichier XML de conteneur, puis transférez le contenu réel sous forme de texte brut dans la partie xml appropriée de l'archive xlsx Zip.)

32
markusk

Une amélioration considérable de l'utilisation de la mémoire peut être obtenue en utilisant un fichier au lieu d'un flux . (Il est préférable d'utiliser une API de streaming, mais les API de streaming ont des limitations, voir http://poi.Apache.org /spreadsheet/index.html )

Donc au lieu de

Workbook workbook = WorkbookFactory.create(inputStream);

faire

Workbook workbook = WorkbookFactory.create(new File("yourfile.xlsx"));

C'est selon: http://poi.Apache.org/spreadsheet/quick-guide.html#FileInputStream

Files vs InputStreams

"Lorsque vous ouvrez un classeur, un HSSFWorkbook .xls ou un .xlsx XSSFWorkbook, le classeur peut être chargé à partir d'un fichier ou d'un InputStream. L'utilisation d'un objet File permet de réduire la consommation de mémoire, tandis qu'un InputStream nécessite davantage de mémoire mettre tout le fichier en mémoire tampon. "

10
rjdkolb

J'avais le même problème avec beaucoup moins de lignes, mais de grosses chaînes.

Comme je n'ai pas besoin de garder mes données chargées, j'ai découvert que je pouvais utiliser SXSSF au lieu de XSSF. 

Ils ont des interfaces similaires, ce qui aide si vous avez beaucoup de code déjà écrit. Mais avec SXSSF, il est possible de définir le nombre de lignes que vous maintenez chargées.

Voici le lien . http://poi.Apache.org/spreadsheet/how-to.html#sxssf

3
DomLavoie

Si vous souhaitez ajuster automatiquement ou définir des styles ou écrire toutes les lignes dans un fichier xlsx volumineux (30 000 lignes), utilisez SXSSFWorkbook.Voici le code exemple qui vous aide ... 

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");
2
Santosh Gdr

J'ai utilisé Event API pour un fichier HSSF (.xls) et j'ai découvert le manque criant de documentation sur l'ordre des enregistrements.

1
ArturoTena

J'ai eu le même problème avec 800 000 cellules et 3M caractères où XSSF alloue 1 Go de tas!

J'ai utilisé Python avec openpyxl et numpy pour lire le fichier xlsx (à partir de code Java) et le convertir d'abord en texte normal. Ensuite, j'ai chargé le fichier texte en Java. Il peut sembler que les frais généraux sont importants, mais c'est en effet rapide.

Le script python ressemble à

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()

Puis dans mon code Java, j'ai écrit

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() );
}

Après cela, vous obtiendrez foo.txt, qui est similaire à foo.xlsx, mais au format texte.

0
mahmood

J'ai utilisé l'analyseur SAX pour traiter la structure XML. Cela fonctionne pour les fichiers XLSX.

https://stackoverflow.com/a/44969009/4587961

0
Yan Khonski

Voici un exemple que j'ai trouvé qui gérera de très gros fichiers XLSX. Mes tests jusqu'à présent semblent bons. Il est capable de gérer des fichiers très volumineux sans problèmes de mémoire.

http://svn.Apache.org/repos/asf/poi/trunk/src/examples/src/org/Apache/poi/xssf/eventusermodel/XLSX2CSV.Java

0
user392243

Si vous écrivez dans XLSX, j'ai trouvé une amélioration en écrivant dans différentes feuilles du même fichier Excel. Vous pouvez également trouver une amélioration en écrivant dans différents fichiers Excel. Mais essayez d'abord d'écrire sur des feuilles différentes.

0
Alexander Mills

Le meilleur exemple à cet égard est décrit dans le thread de débordement de pile suivant: Erreur lors de la lecture de fichiers Excel volumineux (xlsx) via un POI Apache

L'extrait de code dans la réponse principale de cette rubrique illustre les enveloppes de points d'intérêt Apache autour de l'analyse XML SAX et explique comment vous pouvez effectuer une boucle simple sur toutes les feuilles, puis sur chaque cellule.

Le code est obsolète avec l'implémentation actuelle de l'API POI Apache, car l'API endRow () fournit le numéro de ligne actuel qui a fini d'être traité.

Avec cet extrait de code, il devrait être facile d’analyser un fichier XLSX volumineux, cellule par cellule. Par exemple. pour chaque feuille; pour chaque cellule de rangée; row has ends event . Vous pouvez trivialement créer une logique d'application où, à chaque ligne, vous créez une carte de columneName à cellValue.

0
99Sono