2011-02-04 23 views
28

Necesito ajustar automáticamente todas las filas en un archivo xlsx grande (30k + filas).Procesando archivo xlsx grande

El siguiente código a través de poi Apache funciona en archivos pequeños, pero sale con OutOfMemoryError en los grandes:

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

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

workbook.write(outputStream); 

Actualización: Por desgracia, el aumento de tamaño de la pila no es una opción - OutOfMemoryError aparece en -Xmx1024m y 30k filas no es un límite superior.

+0

¿Dónde se está ejecutando el código? Dentro de la aplicación/servidor web o independiente? – JSS

+0

Lo estoy ejecutando dentro de Tomcat 6.0 – miah

+0

¿Cuál es la asignación de memoria predeterminada a Tomcat en el inicio? – JSS

Respuesta

31

Pruebe usar la API del evento. Consulte Event API (HSSF only) y XSSF and SAX (Event API) en la documentación de POI para obtener más información. Un par de citas de esa página:

HSSF:

The event API is newer than the User API. It is intended for intermediate developers who are willing to learn a little bit of the low level API structures. Its relatively simple to use, but requires a basic understanding of the parts of an Excel file (or willingness to learn). The advantage provided is that you can read an XLS with a relatively small memory footprint.

XSSF:

If memory footprint is an issue, then for XSSF, you can get at the underlying XML data, and process it yourself. This is intended for intermediate developers who are willing to learn a little bit of low level structure of .xlsx files, and who are happy processing XML in java. Its relatively simple to use, but requires a basic understanding of the file structure. The advantage provided is that you can read a XLSX file with a relatively small memory footprint.

Para la salida, un posible enfoque se describe en el blog Streaming xlsx files. (Básicamente, utilice XSSF para generar un archivo contenedor XML, luego transmita el contenido real como texto sin formato en la parte xml apropiada del archivo zip xlsx).

+1

Hola, también estoy teniendo el mismo problema de leer archivos grandes de Excel. Salir de problemas de memoria. He visto el http://poi.apache.org/spreadsheet/how-to.html#xssf_sax_api y no especifica cómo leer los archivos de Excel. Por favor ayuda. – ashishjmeshram

+0

@Ashish: publique su solicitud como una pregunta separada sobre Stack Overflow con más detalles. De esa forma, otros usuarios también pueden ayudarlo. – markusk

+0

Para leer archivos grandes de Excel puede echar un vistazo a esta pequeña y simple biblioteca: https://github.com/davidpelfree/sjxlsx –

1

Utilicé la API de eventos para un archivo HSSF (.xls) y Descubrí una terrible falta de documentación sobre el orden de los registros.

+0

Sé que esto es antiguo: pero ¿encontró algo sobre el orden de los eventos en HSSF y/o XSSF? – cripox

3

que estaba teniendo el mismo problema con mucho menos de la fila, pero las grandes cadenas.

Como no tengo que mantener cargados mis datos, descubrí que puedo usar SXSSF en lugar de XSSF.

Tienen interfaces similares, lo que ayuda si ya tiene muchos códigos escritos. Pero con SXSSF es posible establecer la cantidad de filas que mantiene cargadas.

Aquí está el enlace. http://poi.apache.org/spreadsheet/how-to.html#sxssf

10

Una mejora drástica en el uso de la memoria se puede hacer mediante el uso de un archivo en lugar de un flujo. (Es mejor utilizar una API de streaming, pero la API de Transmisión de tener limitaciones, consulte http://poi.apache.org/spreadsheet/index.html)

Así que en lugar de

Workbook workbook = WorkbookFactory.create(inputStream); 

hacer

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

Esto es de acuerdo a: http://poi.apache.org/spreadsheet/quick-guide.html#FileInputStream

Archivos vs InputStreams

"Al abrir un libro de trabajo, ya sea .xls HSSFWorkbook o .xlsx XSSFWorkbook, el libro de trabajo se puede cargar desde un archivo o un InputStream.El uso de un objeto File permite un menor consumo de memoria, mientras que un InputStream requiere más memoria ya que tiene que almacenar todo el archivo. "

+0

Esto me da un error que dice: 'Capturado: java.lang.LinkageError: violación de restricción de cargador: al resolver el método de interfaz" org.xml.sax.XMLReader.setEntityResolver (Lorg/xml/sax/EntityResolver;) V "el cargador de clases (instancia de org/ codehaus/groovy/tools/RootLoader) de la clase actual, org/dom4j/io/SAXReader, y del cargador de clases (instancia de ) para la clase de definición del método, org/xml/sax/XMLReader, tener diferentes objetos de clase C para el tipo org/xml/sax/EntityResolver usado en la firma' Estoy usando 'poi-3.9' – kiltek

+1

@rjdkolb puedes ver mi publicación https://stackoverflow.com/questions/48772021/how -to-resolve-the-java-heap-space-error-while-loading-the-large-xls-file-using-po – Mandrek

0

Si está escribiendo en XLSX, encontré una mejora al escribir en diferentes hojas del mismo archivo Excel. También puede encontrar una mejora al escribir en diferentes archivos de Excel. Pero primero intente escribir en diferentes hojas.

2

Si desea ajustar automáticamente o establecer estilos o escribir todas las filas en grande (30k + filas) xlsx archivo, use SXSSFWorkbook.Aquí está el código de ejemplo que le ayuda ...

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

El mejor ejemplo de esto se describe en el siguiente hilo de desbordamiento de pila: Error While Reading Large Excel Files (xlsx) Via Apache POI

El fragmento de código en la respuesta principal de ese tema ilustra las envolturas de Apache POI alrededor de análisis sintáctico XML SAX, y cómo se puede trivialmente bucle sobre todo el hojas y luego sobre cada celda individual.

El código está obsoleto con la implementación actual de la API de POI de Apache, ya que la API endrow() proporciona el número de fila actual que ha terminado de procesarse.

Con ese fragmento de código debe ser trivial para analizar un gran archivo XLSX celda por celda. P.ej. para cada hoja; para cada celda de fila; fila ha terminado el evento. Puede crear trivialmente la lógica de la aplicación donde en cada fila crea un Mapa de columneName a cellValue.

0

Tuve el mismo problema con 800,000 celdas y 3M caracteres donde XSSF asigna 1GB de montón.

Utilicé Python con openpyxl y numpy para leer el archivo xlsx (desde código Java) y primero convertirlo a texto normal. Luego cargué el archivo de texto en java. Puede parecer tener grandes gastos generales, pero de hecho es rápido.

El script en Python parece

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

Luego, en mi código java, escribí

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

Después de eso, obtendrá foo.txt que es similar a foo.xlsx, pero en formato de texto.