Generation and download of Excel files

1. Defects of Apache open source framework poi and jxl

Both have the problem that the generation of excel files is not simple, elegant and fast enough. Moreover, they all have a serious problem, that is, they consume a lot of memory, and in severe cases, they will cause memory overflow. Although POI is currently the most widely used excel parsing framework, this framework is not perfect. Why do you say that?

Developers mostly use POI, using its userModel mode. The advantage of userModel is that it is easy to use and easy to use. Just copy a code and run it, and the rest is to write business conversion. Although the conversion requires writing hundreds of lines of code, it is still controllable.

However, the biggest problem with the userModel mode is that it consumes a lot of memory, and parsing a few megabytes of files even consumes hundreds of megabytes of memory. The reality is that many applications are now using this mode. The reason why they are still running normally is because the concurrency is not large. After the concurrency is up, there will be OOM or frequent FULL GC .

2. Ali's EasyExcel

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

3. What does EasyExcel solve?

  • Traditional Excel frameworks, such as Apache poi and jxl, have memory overflow problems;
  • Traditional excel open source framework is complex and cumbersome to use;
  • The bottom layer of EasyExcel still uses poi, but it has done a lot of optimizations, such as fixing some bugs in concurrent situations. For details of the repair, you can read the official document https://github.com/alibaba/easyexcel

Four, pom dependency

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

5. Code example

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

In the sample code above, two points are very important:
①WriteModel object is the data model object to be written into Excel (header, and the order of data in each cell will be described below)
②Create the data set to be written , (In normal business, this piece is all queried from the database)

1️⃣createModelList

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️⃣WriteModel

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 provides annotations to easily define the data model required by Excel:
①First, the defined write model must inherit from BaseRowModel.java;
②Specify the column name and subscript position of each field through the @ExcelProperty annotation

6. Dynamically generate Excel content

The above example is based on annotations, that is to say, the head and content of the table are hard-coded. In other words, after a data model is defined, the generated Excel file can only follow this model. However, there may be dynamically changing requirements in actual business, what should be done?

   @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️⃣ No annotation mode, dynamic addition of headers, and free combination of complex headers, the code is as follows:

 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️⃣Create dynamic data, note that the data type here is Object:

 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️⃣ Customize the header and content style

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

Seven, custom processing

For more complex processing, EasyExcel reserves the WriterHandler interface, allowing you to customize the processing code:

Three methods are defined in the interface:

  • sheet(): Customize business logic processing after creating each sheet;
  • row(): Customize business logic processing after creating each row;
  • cell(): Customize business logic processing after creating each cell;

After we implement this interface, write custom logic processing code, and then call getWriterWithTempAndHandler() static method to get ExcelWriter object, just pass in the implementation class of WriterHandler.

example:

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

Eight, Web download sample code

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. Points to pay attention to

1️⃣ When writing large data, fragmentation is required. For example, when a large amount of data is queried from the database, fragmentation processing needs to be done at the business layer, that is, it needs to be queried multiple times and then written to prevent memory overflow OOM.

2️⃣The problem of the maximum number of rows in Excel.
Both Excel 03 and 07 versions have restrictions on the number of rows and columns:

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

Since csv is a text file, there is actually no limit to the maximum number of rows, but it still cannot be displayed if it is opened with the Excel client. In other words, if you want to write more lines, it is not acceptable. If you force it, the program will report an exception similar to the following:
Invalid row number (1048576) outside allowable range (0..1048575)

How to solve it?
Write in multiple Excel files;
write in multiple Sheets for the same Excel file.

Guess you like

Origin blog.csdn.net/ChineseSoftware/article/details/123814045