Generación y descarga de archivos Excel

1. Defectos del marco de código abierto de Apache poi y jxl

Ambos tienen el problema de que la generación de archivos de Excel no es lo suficientemente simple, elegante y rápida. Además, todos tienen un problema grave, es decir, consumen mucha memoria y, en casos graves, provocarán un desbordamiento de memoria. Aunque POI es actualmente el marco de análisis de Excel más utilizado, este marco no es perfecto. ¿Por qué dices eso?

Los desarrolladores usan principalmente POI, usando su modo de modelo de usuario. La ventaja de userModel es que es fácil de usar y fácil de usar. Simplemente copie un código y ejecútelo, y el resto es escribir la conversión comercial. Aunque la conversión requiere escribir cientos de líneas de código, todavía es controlable.

Sin embargo, el mayor problema con el modo de modelo de usuario es que consume mucha memoria y analizar algunos megabytes de archivos incluso consume cientos de megabytes de memoria. La realidad es que muchas aplicaciones ahora están usando este modo. La razón por la que todavía se ejecutan normalmente es porque la concurrencia no es grande. Después de que la concurrencia esté activa, habrá OOM o FULL GC frecuentes .

2. EasyExcel de Ali

官方对其的简介是:快速、简单避免OOM的java处理Excel工具。

3. ¿Qué soluciona EasyExcel?

  • Los marcos tradicionales de Excel, como Apache poi y jxl, tienen problemas de desbordamiento de memoria;
  • El marco tradicional de código abierto de Excel es complejo y engorroso de usar;
  • La capa inferior de EasyExcel todavía usa poi, pero ha realizado muchas optimizaciones, como corregir algunos errores en situaciones concurrentes. Para obtener detalles de la reparación, puede leer el documento oficial https://github.com/alibaba/easyexcel

Cuatro, dependencia pom

<!--alibaba easyexcel-->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>easyexcel</artifactId>
  <version>1.1.2-beta5</version>
</dependency>

5. Ejemplo de código

@Test
public void writeExcel() throws Exception {
    
    

    // 文件输出位置
    OutputStream out = new FileOutputStream("d:/test.xlsx");
    ExcelWriter writer = EasyExcelFactory.getWriter(out);

    // 写仅有一个 Sheet 的 Excel 文件, 此场景较为通用
    Sheet sheet = new Sheet(1, 0, WriteModel.class);

    // 第一个 sheet 名称
    sheet.setSheetName("first sheet");

    // 写数据到 Writer 上下文中
    // 入参1: 创建要写入的模型数据
    // 入参2: 要写入的目标 sheet
    writer.write(createModelList(), sheet);

    // 将上下文中的最终 outputStream 写入到指定文件中
    writer.finish();
    // 关闭流
    out.close();
}

En el código de muestra anterior, dos puntos son muy importantes:
①El objeto WriteModel es el objeto del modelo de datos que se escribirá en Excel (el encabezado y el orden de los datos en cada celda se describirán a continuación)
②Crear el conjunto de datos que se escribirá, (En negocios normales, esta pieza se consulta desde la base de datos)

1️⃣crear lista de modelos

private List<WriteModel> createModelList() {
    
    

    List<WriteModel> writeModels = new ArrayList<>();
     for (int i = 0; i < 100; i++) {
    
    
        WriteModel writeModel = WriteModel.builder()
                .name("test" + i).password("123456")
                .age(i + 1).build();
        writeModels.add(writeModel);
    }
    return writeModels;
}

2️⃣Modelo de escritura

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.BaseRowModel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class WriteModel extends BaseRowModel {
    
    

    @ExcelProperty(value = "姓名", index = 0)
    private String name;

    @ExcelProperty(value = "密码", index = 1)
    private String password;

    @ExcelProperty(value = "年龄", index = 2)
    private Integer age;
}

ExayExcel proporciona anotaciones para definir fácilmente el modelo de datos requerido por Excel:
①Primero, el modelo de escritura definido debe heredar de BaseRowModel.java;
②Especifique el nombre de la columna y la posición del subíndice de cada campo a través de la anotación @ExcelProperty

6. Genera dinámicamente contenido de Excel

El ejemplo anterior se basa en anotaciones, es decir, el encabezado y el contenido de la tabla están codificados, en otras palabras, después de que se define un modelo de datos, el archivo de Excel generado solo puede seguir este modelo. Sin embargo, puede haber requisitos que cambien dinámicamente en el negocio real, ¿qué se debe hacer?

   @Test
    public void writeExcel() throws Exception {
    
    

        // 文件输出位置
        OutputStream out = new FileOutputStream("d:/dynamic.xlsx");
        ExcelWriter writer = EasyExcelFactory.getWriter(out);

        // 动态添加表头,适用一些表头动态变化的场景
        Sheet sheet = new Sheet(1, 0);

        sheet.setSheetName("first sheet");

        // 创建一个表格,用于 Sheet 中使用
        Table table = new Table(1);
        //自定义表格样式
        table.setTableStyle(createTableStyle());
        // 无注解的模式,动态添加表头
        table.setHead(createTestListStringHead());
        // 写数据
        writer.write1(createDynamicModelList(), sheet, table);

        //可以通过 merge()方法来合并单元格
        //注意下标是从 0 开始的,也就是说合并了第六行到第七行,其中的第一列到第五列
        writer.merge(5, 6, 0, 4);

        // 将上下文中的最终 outputStream 写入到指定文件中
        writer.finish();

        // 关闭流
        out.close();
    }

1️⃣ Sin modo de anotación, adición dinámica de encabezados y combinación libre de encabezados complejos, el código es el siguiente:

 public static List<List<String>> createTestListStringHead() {
    
    
        // 模型上没有注解,表头数据动态传入
        List<List<String>> head = new ArrayList<List<String>>();
        List<String> headCoulumn1 = new ArrayList<String>();
        List<String> headCoulumn2 = new ArrayList<String>();
        List<String> headCoulumn3 = new ArrayList<String>();
        List<String> headCoulumn4 = new ArrayList<String>();
        List<String> headCoulumn5 = new ArrayList<String>();

        headCoulumn1.add("第一列");
        headCoulumn1.add("第一列");
        headCoulumn1.add("第一列");
        headCoulumn2.add("第一列");
        headCoulumn2.add("第一列");
        headCoulumn2.add("第一列");

        headCoulumn3.add("第二列");
        headCoulumn3.add("第二列");
        headCoulumn3.add("第二列");
        headCoulumn4.add("第三列");
        headCoulumn4.add("第三列2");
        headCoulumn4.add("第三列2");
        headCoulumn5.add("第四列");
        headCoulumn5.add("第4列");
        headCoulumn5.add("第5列");

        head.add(headCoulumn1);
        head.add(headCoulumn2);
        head.add(headCoulumn3);
        head.add(headCoulumn4);
        head.add(headCoulumn5);
        return head;
    }

2️⃣Cree datos dinámicos, tenga en cuenta que el tipo de datos aquí es Objeto:

 private List<List<Object>> createDynamicModelList() {
    
    
        //所有行数据
        List<List<Object>> rows = new ArrayList<>();

        for (int i = 0; i < 100; i++) {
    
    
            //一行数据
            List<Object> row = new ArrayList<>();
            row.add("第" + i+"行");
            row.add(Long.valueOf(187837834L + i));
            row.add(Integer.valueOf(2233 + i));
            row.add("NMB");
            row.add("CBF");
            rows.add(row);
        }
        return rows;
    }

3️⃣ Personaliza el encabezado y el estilo del contenido

public static TableStyle createTableStyle() {
    
    
        TableStyle tableStyle = new TableStyle();
        // 设置表头样式
        Font headFont = new Font();
        // 字体是否加粗
        headFont.setBold(true);
        // 字体大小
        headFont.setFontHeightInPoints((short) 12);
        // 字体
        headFont.setFontName("楷体");
        tableStyle.setTableHeadFont(headFont);
        // 背景色
        tableStyle.setTableHeadBackGroundColor(IndexedColors.BLUE);

        // 设置表格主体样式
        Font contentFont = new Font();
        contentFont.setBold(true);
        contentFont.setFontHeightInPoints((short) 12);
        contentFont.setFontName("黑体");
        tableStyle.setTableContentFont(contentFont);
        tableStyle.setTableContentBackGroundColor(IndexedColors.GREEN);
        return tableStyle;
    }

Siete, procesamiento personalizado

Para procesamientos más complejos, EasyExcel reserva la interfaz WriterHandler, que le permite personalizar el código de procesamiento:

En la interfaz se definen tres métodos:

  • hoja (): personalice el procesamiento de la lógica empresarial después de crear cada hoja;
  • fila (): personalice el procesamiento de la lógica empresarial después de crear cada fila;
  • celda (): personalice el procesamiento de la lógica de negocios después de crear cada celda;

Después de implementar esta interfaz, escriba el código de procesamiento de lógica personalizado y luego llame al método estático getWriterWithTempAndHandler() para obtener el objeto ExcelWriter, simplemente pase la clase de implementación de WriterHandler.

ejemplo:

ExcelWriter writer = EasyExcelFactory
.getWriterWithTempAndHandler(null, out, ExcelTypeEnum.XLSX, true, new MyWriterHandler());

Ocho, código de muestra de descarga web

public class Down {
    
    
    @GetMapping("/a.htm")
    public void cooperation(HttpServletRequest request, HttpServletResponse response) {
    
    
        ServletOutputStream out = response.getOutputStream();
        ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX, true);
        String fileName = new String(("UserInfo " + new SimpleDateFormat("yyyy-MM-dd").format(new Date()))
                .getBytes(), "UTF-8");
        Sheet sheet1 = new Sheet(1, 0);
        sheet1.setSheetName("第一个sheet");
        writer.write0(getListString(), sheet1);
        writer.finish();
        response.setContentType("multipart/form-data");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename="+fileName+".xlsx");
        out.flush();
        }
    }

9. Puntos a los que prestar atención

1️⃣ Al escribir datos grandes, se requiere fragmentación. Por ejemplo, cuando se consulta una gran cantidad de datos desde la base de datos, el procesamiento de fragmentación debe realizarse en la capa empresarial, es decir, debe consultarse varias veces y luego escribirse para evitar el desbordamiento de memoria OOM.

2️⃣El problema del número máximo de filas en Excel.
Ambas versiones de Excel 03 y 07 tienen restricciones en el número de filas y columnas:

版本	            最大行	           最大列
Excel 2003	      65536	            256
Excel 2007	      1048576	        16384

Dado que csv es un archivo de texto, en realidad no hay límite para el número máximo de filas, pero aún no se puede mostrar si se abre con el cliente de Excel. En otras palabras, si desea escribir más líneas, no es aceptable, si lo fuerza, el programa reportará una excepción similar a la siguiente:
Invalid row number (1048576) outside allowable range (0..1048575)

¿Cómo resolverlo?
Escriba en varios archivos de Excel;
escriba en varias hojas para el mismo archivo de Excel.

Supongo que te gusta

Origin blog.csdn.net/ChineseSoftware/article/details/123814045
Recomendado
Clasificación