Domanda

Devo adattare automaticamente tutte le righe in file XLSX di grandi dimensioni (30k+ righe).

Il seguente codice tramite Apache Poi funziona su piccoli file, ma esce con OutOfMemoryError su quelli grandi:

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

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

workbook.write(outputStream);

Aggiornare: Sfortunatamente, aumentare la dimensione del heap non è un'opzione - OutOfMemoryError appare a -Xmx1024m e 30k righe non è un limite superiore.

È stato utile?

Soluzione

Prova a usare l'API dell'evento. Vedere Evento API (solo HSSF) e XSSF e SAX (API Event) Nella documentazione POI per i dettagli. Un paio di citazioni da quella pagina:

HSSF:

L'API dell'evento è più recente dell'API utente. È destinato a sviluppatori intermedi che sono disposti a imparare un po 'delle strutture API di basso livello. È relativamente semplice da usare, ma richiede una comprensione di base delle parti di un file Excel (o volontà di imparare). Il vantaggio fornito è che puoi leggere un XLS con un'impronta di memoria relativamente piccola.

XSSF:

Se l'impronta della memoria è un problema, quindi per XSSF, puoi ottenere i dati XML sottostanti ed elaborarli da solo. Questo è destinato a sviluppatori intermedi che sono disposti a imparare un po 'di struttura di basso livello di file .xlsx e che sono felici elaborare XML in Java. È relativamente semplice da usare, ma richiede una comprensione di base della struttura dei file. Il vantaggio fornito è che è possibile leggere un file XLSX con un'impronta di memoria relativamente piccola.

Per l'output, un possibile approccio è descritto nel post sul blog Streaming di file XLSX. (Fondamentalmente, utilizzare XSSF per generare un file XML contenitore, quindi trasmettere il contenuto effettivo come testo normale nella parte XML appropriata dell'archivio zip XLSX.)

Altri suggerimenti

È possibile eseguire un drammatico miglioramento dell'utilizzo della memoria utilizzando un file anziché un flusso. (È meglio utilizzare un'API di streaming, ma le API di streaming hanno limiti, vedere http://poi.apache.org/spreadsheet/index.html)

Quindi invece di

Workbook workbook = WorkbookFactory.create(inputStream);

fare

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

Questo è secondo: http://poi.apache.org/spreadsheet/quick-guide.html#fileinputstream

File vs InputStreams

"Quando si apri una cartella di lavoro, un .xls hssfworkbook o un .xlsx xssfworkbook, la cartella di lavoro può essere caricata da un file o da un inputStream. L'uso di un oggetto file consente un consumo di memoria inferiore, mentre un inputstream richiede più memoria in quanto ha per buffer l'intero file. "

Stavo avendo lo stesso problema con molto meno di fila, ma stringhe di grandi dimensioni.

Dal momento che non devo mantenere i miei dati caricati, ho scoperto che posso usare SXSF invece di XSSF.

Hanno interfacce simili, che aiutano se hai un sacco di codice già scritto. Ma con SXSF è possibile impostare la quantità di righe che tieni caricate.

Ecco il link.http://poi.apache.org/spreadsheet/how-to.html#sxssf

Se si desidera adattare automaticamente o impostare stili o scrivere tutte le righe in file xlsx di grandi dimensioni (30k+ righe), usa sxssfworkbook. Ecco il codice di esempio che ti aiuta ...

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");

Ho usato l'API di eventi per un file HSSF (.xls) e ho scoperto la terribile mancanza di documentazione sull'ordine dei registri.

Se sei scrivere A XLSX, ho trovato un miglioramento scrivendo su diversi fogli dello stesso file Excel. Potresti anche trovare un miglioramento scrivendo a diversi file di Excel. Ma prima prova a scrivere a diversi fogli.

Il miglior esempio per questo è descritto nel seguente thread di overflow dello stack:Errore durante la lettura di grandi file Excel (XLSX) tramite Apache POI

Lo snippet di codice nella risposta principale in quell'argomento illustra le involucri di Apache POI attorno all'analisi XML di Sax e come è possibile eseguire banale su tutti i fogli e quindi su ogni singola cella.

Il codice è stantio con l'attuale implementazione dell'API Apache POI, poiché l'API Endrow () fornisce il numero di riga corrente che ha terminato l'elaborazione.

Con quel frammento di codice dovrebbe essere banale per il tuo analizzare una grande cella di file XLSX per cella. Ad esempio per ogni foglio; per ogni cella di riga; La riga ha terminato l'evento. È possibile creare banale la logica dell'app dove in ogni riga si crea una mappa di Columnename a CellValue.

Ho avuto lo stesso problema con 800.000 celle e caratteri 3M in cui XSSF assegna 1 GB di heap!

Ho usato Python con openpyxl e numpy Per leggere il file XLSX (dal codice Java) e prima convertilo in un testo normale. Quindi ho caricato il file di testo in Java. Potrebbe sembrare un grande sovraccarico, ma è davvero veloce.

La sceneggiatura di Python sembra

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

Poi nel mio codice Java, ho scritto

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

Successivamente, otterrai foo.txt che è simile a foo.xlsx, ma in formato di testo.

Ho usato il parser sax per elaborare la struttura XML. Funziona per i file XLSX.

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top