¿Cómo lidiar con la falta de memoria al usar PDI para abrir un archivo de Excel?

Resumen: Este artículo fue publicado originalmente en CSDN por el equipo técnico de Grape City. Indique la fuente de la reimpresión: sitio web oficial de Grape City , Grape City proporciona a los desarrolladores herramientas, soluciones y servicios de desarrollo profesional para empoderar a los desarrolladores.

Apache POI fue la herramienta elegida por muchos cuando desarrollamos para manejar archivos de Excel. Sin embargo, con el aumento de los requisitos y la complejidad del proyecto, pueden ocurrir algunas situaciones anormales al abrir archivos complejos de Excel.

De acuerdo con la prueba, al abrir 500 000 datos de celdas, encontrará problemas de OOM (OutOfMemory); o al abrir 200 000 celdas combinadas (incluido el borde o el color de fondo), también encontrará problemas de OOM (OutOfMemory).

Usando WorkbookFactory para abrir el archivo de Excel directamente, el código es el siguiente:

File file = new File("testFile.xlsx");

Workbook workbook = WorkbookFactory.create(file);

//打开文件后进行其他处理

El código anterior causará problemas de OOM al procesar archivos de Excel grandes.

He buscado en internet y hay dos formas:

  1. El archivo se puede convertir a CSV y luego importar.
  2. Diseñe archivos de Excel en pequeños archivos de Excel, cree libros de trabajo por separado y luego procéselos.

El primer método funciona bien cuando solo se importan datos. Pero cuando Excel tiene un estilo, la conversión de Excel a CSV hará que se pierda el estilo, por lo que se pasa este método.

Parece que se puede considerar el segundo método, que consiste en dividir el archivo en varios archivos pequeños, crear libros de trabajo por separado y luego procesarlos.

Así que dividí manualmente el archivo de Excel, cambié el código brevemente y lo probé.

File file = new File("test.xlsx");

File file1 = new File("test1.xlsx");

File file2 = new File("test2.xlsx");

File file3 = new File("test3.xlsx");

File file4 = new File("test4.xlsx");

File file5 = new File("test5.xlsx");

File file6 = new File("test6.xlsx");

Workbook workbook = WorkbookFactory.create(file);

Workbook workbook1 = WorkbookFactory.create(file1);

Workbook workbook2 = WorkbookFactory.create(file2);

Workbook workbook3 = WorkbookFactory.create(file3);

Workbook workbook4 = WorkbookFactory.create(file4);

Workbook workbook5 = WorkbookFactory.create(file5);

Workbook workbook6 = WorkbookFactory.create(file6);

Pero todavía encontré problemas, y todavía había un problema de oom. Utilicé la prueba unitaria para hacer la prueba, y el contenido del error es el siguiente:

...

at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)

at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)

at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)

at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)

at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)

Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded

at java.util.Arrays.copyOfRange(Arrays.java:3664)

at java.lang.String.\<init\>(String.java:207)

at com.sun.org.apache.xerces.internal.xni.XMLString.toString(XMLString.java:190)

at com.sun.org.apache.xerces.internal.util.XMLAttributesImpl.getValue(XMLAttributesImpl.java:523)

at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser\$AttributesProxy.getValue(AbstractSAXParser.java:2321)

...

Después de algunos intentos, se descubrió que había demasiados libros de trabajo creados al mismo tiempo. Cuando el número se redujo a 4, la prueba unitaria pudo ejecutarse normalmente.

Desde este punto de vista, el problema de los puntos de interés es realmente un dolor de cabeza. Al realizar pruebas, es posible saber en cuántos archivos se dividen, pero en la aplicación real, es imposible predecir la cantidad de archivos. Además, según la prueba, la cantidad de libros de trabajo puede estar relacionada con el tamaño del archivo de Excel, lo que puede causar más problemas en el desarrollo posterior.

Continúe navegando por Internet y verá que además del método de optimización de POI, también hay otros productos como EasyExcel y GcExcel.

Después de una breve verificación, EasyExcel es de código abierto, principalmente porque hace un buen trabajo en escenarios de lectura y escritura de alta concurrencia. GcExcel es un software comercial con una API completa.

Entonces puedes usar estos dos componentes para verificarlo Hay dos problemas principales que queremos resolver:

  1. Los archivos de Excel con una gran cantidad de datos y estilos se pueden abrir a la vez
  2. Hay una manera de preservar el estilo o manipular el estilo de copia.

Para la pregunta 1, tanto EasyExcel como GcExcel funcionan muy bien y no hay problema de OOM. El estilo de los dos componentes no es el mismo en el código. GcExcel y POI son similares y construyen directamente el libro de trabajo. El ejemplo dado por POI es a través de anotaciones, que es más como una experiencia de deserialización.Al mismo tiempo, se escribe un oyente para cada lectura y se procesa una lógica especial a través del oyente.

Para la pregunta 2, escribí sobre UT y los códigos son los siguientes:

Eche un vistazo a EasyExcel primero,

Primero, EasyExcel necesita definir una clase de datos para leer datos.

@Getter

@Setter

@EqualsAndHashCode

public class DemoData {
    
    

private String cell1;

private String cell2;

}

Defina una clase de oyente, y la lógica de procesamiento del estilo debe procesarse en invocar. No encontré la API relacionada con EasyExcel, ni usé la API de POI para procesar el contenido relacionado con el estilo.

@Slf4j

public class DemoListener implements ReadListener\<DemoData\> {
    
    

private int rowNum = 0;

private Sheet sheet;

@Override

public void invoke(DemoData data, AnalysisContext context) {
    
    

if (sheet == null) {
    
    

sheet = (Sheet) context.readSheetHolder().getReadSheet();

}

Row row = sheet.getRow(rowNum);

// 获取第一列

Cell cell0 = row.getCell(0);

CellStyle style0 = cell0.getCellStyle();

// 创建样式对象

Workbook workbook = sheet.getWorkbook();

CellStyle newStyle = workbook.createCellStyle();

// 复制原有样式到新创建的样式对象中

newStyle.cloneStyleFrom(style0);

// TODO: 其他操作

rowNum++;

}

@Override

public void doAfterAllAnalysed(AnalysisContext context) {
    
    

}

}

Desde el sitio web oficial, después de EasyExcel 2.0.0-beta1, puede usar el método adicional para obtener comentarios, hipervínculos e información de celda combinada. Pero si hay un borde u otros estilos, parece que este método no se puede usar.

Después de una prueba simple, el problema se puede resolver, pero el estilo es aún más complicado de manejar.

Para GcExcel, según el libro de códigos de documentación oficial, es muy simple. Basado directamente en el concepto de rango, se pueden obtener varios estilos a través del método set/get.
https://www.grapecity.com.cn/developer/grapecitydocuments/excel-java/docs/Features/ApplyStyle

Hagamos una prueba simple, es muy simple de usar, siempre que comprenda los conceptos relacionados con Excel, puede obtener el estilo fácilmente.

@Test

public void testRepeatCreateObject() throws IOException {
    
    

String fileName = "test.xlsx";

Workbook workbook = new Workbook();

workbook.open(fileName);

IWorksheet sheet = workbook.getWorksheets().get(0);

IStyle style = sheet.getRange(0,0).getStyle();

System.out.println("font "+style.getFont().getName());

System.out.println("border "+style.getBorders().getLineStyle().name());

}

Hasta ahora, en general, si te gusta usar código abierto, puedes elegir EasyExcel. EasyExcel proporciona el mismo método de anotación que la deserialización para leer datos. Es simple en términos de lectura de datos. Pero en términos de procesamiento de estilo, debe confiar en el mecanismo de eventos para manejarlo, lo que sigue siendo un poco problemático.

Si está desarrollando un proyecto comercial, puede considerar GcExcel. La API de GcExcel es muy simple y fácil de usar, además en la prueba se encuentra que la velocidad de apertura de archivos es mucho más rápida lo que puede reducir el costo de desarrollo.

Enlace de extensión:

Importar y exportar Excel en el lado del servidor

Cómo usar C1 para realizar la interacción entre el programa de aplicación y Microsoft Excel

Tutorial de desarrollo de informes complejos al estilo chino (1): tabulación cruzada unidimensional similar a Excel

Supongo que te gusta

Origin blog.csdn.net/powertoolsteam/article/details/131422330
Recomendado
Clasificación