Java uses EasyExcel to export excel according to the template

EasyExcel

The well-known frameworks for Java parsing and generating Excel include Apache poi and jxl. But they all have a serious problem that consumes a lot of memory. poi has a SAX mode API that can solve some memory overflow problems to a certain extent, but POI still has some defects. It is done in memory, and the memory consumption is still very large.

easyexcel rewrites poi's analysis of Excel version 07. A 3M excel with POI sax analysis still needs about 100M of memory. Using easyexcel can reduce it to a few M, and no matter how big the excel is, there will be no memory overflow; version 03 depends on POI's sax mode encapsulates the model conversion on the upper layer, making it easier and more convenient for users. Official website

16M memory and 23 seconds to read 75M (46W rows and 25 columns) Excel (version 3.2.1+)

insert image description here

import dependencies

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.2.1</version>
</dependency>

template file

The variables in the template file correspond to the attributes of the entity class. The type {.aaa} is a collection, and the type {aaa} is an object.

insert image description here

code example

package com.test.controller;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.test.domain.Project;
import com.test.utils.ExcelFillCellMergeStrategy;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.*;

/**
 * 
 * 
 * @author linfeng
 * @date 2023-03-25 17:49
 */
@Controller
@RequestMapping("/excel/test")
public class ExcelTestController {
    
    



    @GetMapping("/export")
    public void export(HttpServletResponse response) throws Exception{
    
    
        String templateFilePath = "C:\\Users\\DELL\\Desktop\\2023年度重点洽谈项目汇总表.xlsx";

        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");

        String fileName = URLEncoder.encode("2023年度重点洽谈项目汇总表"+System.currentTimeMillis()/1000+".xlsx", "utf-8");

        response.setHeader("Content-disposition", "attachment; filename=" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1"));

        //设置第几列开始合并
        int[] mergeColumnIndex = {
    
    0};
        //设置第几行开始合并
        int mergeRowIndex = 2;

        OutputStream outputStream = response.getOutputStream();
        BufferedOutputStream bos = new BufferedOutputStream(outputStream);
        ExcelWriter excelWriter = EasyExcel.write(bos).withTemplate(templateFilePath)
                .registerWriteHandler(new ExcelFillCellMergeStrategy(mergeRowIndex,mergeColumnIndex))
                .build();
        WriteSheet writeSheet = EasyExcel.writerSheet().build();

        Map<String,Object> excelDataMap = new HashMap<>(16);

        List<Project> projectList = getProjectList();

        excelDataMap.put("year",2023);
        excelWriter.fill(excelDataMap,writeSheet);
        excelWriter.fill(projectList,writeSheet);

        excelWriter.finish();
        bos.flush();

    }
    

    public static List<Project> getProjectList(){
    
    
        List<Project> projectList = new ArrayList<>();
        projectList.add(new Project("成华区","计划洽谈",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("成华区","进行中",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("成华区","完成",20L,29L,2L,5L,30L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("武侯区","计划洽谈",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("武侯区","进行中",20L,10L,2L,5L,78L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("武侯区","完成",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("高新区","计划洽谈",60L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("高新区","进行中",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("高新区","完成",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("锦江区","计划洽谈",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("锦江区","进行中",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("锦江区","完成",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("金牛区","计划洽谈",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("金牛区","进行中",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("金牛区","完成",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("龙泉区","计划洽谈",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("龙泉区","进行中",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("龙泉区","完成",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("青羊区","计划洽谈",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("青羊区","进行中",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("青羊区","完成",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("温江区","计划洽谈",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("温江区","进行中",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));
        projectList.add(new Project("温江区","完成",20L,10L,2L,5L,100L,200L,10L,98L,34L,43L,45L,90L));


        return projectList;
    }

}

Entity class

public class Project {
    
    

    /** 项目名称 */
    private String projectName;

    /** 项目类型 */
    private String projectType;

    /** 一月数据量 */
    private Long januaryNumber;

    /** 二月数据量 */
    private Long februaryNumber;

    /** 三月数据量 */
    private Long marchNumber;

    /** 四月数据量 */
    private Long aprilNumber;

    /** 五月数据量 */
    private Long mayNumber;

    /** 六月数据量 */
    private Long juneNumber;

    /** 七月数据量 */
    private Long julyNumber;

    /** 八月数据量 */
    private Long augustNumber;

    /** 九月数据量 */
    private Long septemberNumber;

    /** 十月数据量 */
    private Long octoberNumber;

    /** 十一月数据量 */
    private Long novemberNumber;

    /** 十二月数据量 */
    private Long decemberNumber;

	public Project() {
    
    }

    public Project(String projectName, String projectType, Long januaryNumber, Long februaryNumber, Long marchNumber, Long aprilNumber, Long mayNumber, Long juneNumber, Long julyNumber, Long augustNumber, Long septemberNumber, Long octoberNumber, Long novemberNumber, Long decemberNumber) {
    
    
        this.projectName = projectName;
        this.projectType = projectType;
        this.januaryNumber = januaryNumber;
        this.februaryNumber = februaryNumber;
        this.marchNumber = marchNumber;
        this.aprilNumber = aprilNumber;
        this.mayNumber = mayNumber;
        this.juneNumber = juneNumber;
        this.julyNumber = julyNumber;
        this.augustNumber = augustNumber;
        this.septemberNumber = septemberNumber;
        this.octoberNumber = octoberNumber;
        this.novemberNumber = novemberNumber;
        this.decemberNumber = decemberNumber;
    }

    public String getProjectName() {
    
    
        return projectName;
    }

    public void setProjectName(String projectName) {
    
    
        this.projectName = projectName;
    }

    public String getProjectType() {
    
    
        return projectType;
    }

    public void setProjectType(String projectType) {
    
    
        this.projectType = projectType;
    }

    public Long getJanuaryNumber() {
    
    
        return januaryNumber;
    }

    public void setJanuaryNumber(Long januaryNumber) {
    
    
        this.januaryNumber = januaryNumber;
    }

    public Long getFebruaryNumber() {
    
    
        return februaryNumber;
    }

    public void setFebruaryNumber(Long februaryNumber) {
    
    
        this.februaryNumber = februaryNumber;
    }

    public Long getMarchNumber() {
    
    
        return marchNumber;
    }

    public void setMarchNumber(Long marchNumber) {
    
    
        this.marchNumber = marchNumber;
    }

    public Long getAprilNumber() {
    
    
        return aprilNumber;
    }

    public void setAprilNumber(Long aprilNumber) {
    
    
        this.aprilNumber = aprilNumber;
    }

    public Long getMayNumber() {
    
    
        return mayNumber;
    }

    public void setMayNumber(Long mayNumber) {
    
    
        this.mayNumber = mayNumber;
    }

    public Long getJuneNumber() {
    
    
        return juneNumber;
    }

    public void setJuneNumber(Long juneNumber) {
    
    
        this.juneNumber = juneNumber;
    }

    public Long getJulyNumber() {
    
    
        return julyNumber;
    }

    public void setJulyNumber(Long julyNumber) {
    
    
        this.julyNumber = julyNumber;
    }

    public Long getAugustNumber() {
    
    
        return augustNumber;
    }

    public void setAugustNumber(Long augustNumber) {
    
    
        this.augustNumber = augustNumber;
    }

    public Long getSeptemberNumber() {
    
    
        return septemberNumber;
    }

    public void setSeptemberNumber(Long septemberNumber) {
    
    
        this.septemberNumber = septemberNumber;
    }

    public Long getOctoberNumber() {
    
    
        return octoberNumber;
    }

    public void setOctoberNumber(Long octoberNumber) {
    
    
        this.octoberNumber = octoberNumber;
    }

    public Long getNovemberNumber() {
    
    
        return novemberNumber;
    }

    public void setNovemberNumber(Long novemberNumber) {
    
    
        this.novemberNumber = novemberNumber;
    }

    public Long getDecemberNumber() {
    
    
        return decemberNumber;
    }

    public void setDecemberNumber(Long decemberNumber) {
    
    
        this.decemberNumber = decemberNumber;
    }
}

Merge cell tool class

The prerequisites for merging cells are the same data. In the test data I wrote above, there are three cell data that are the same.

package com.test.utils;

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.List;

/**
 * @author linfeng
 * @date 2023-03-25 21:06
 */
public class ExcelFillCellMergeStrategy implements CellWriteHandler {
    
    

    /**
     * 合并字段的下标,如第一到五列new int[]{0,1,2,3,4}
     */
    private int[] mergeColumnIndex;
    /**
     * 从第几行开始合并,如果表头占两行,这个数字就是2
     */
    private int mergeRowIndex;

    public ExcelFillCellMergeStrategy() {
    
    
    }

    public ExcelFillCellMergeStrategy(int mergeRowIndex, int[] mergeColumnIndex) {
    
    
        this.mergeRowIndex = mergeRowIndex;
        this.mergeColumnIndex = mergeColumnIndex;
    }

    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
                                 Head head, Integer integer, Integer integer1, Boolean aBoolean) {
    
    

    }

    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell,
                                Head head, Integer integer, Boolean aBoolean) {
    
    

    }



    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
                                 List<WriteCellData<?>> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {
    
    
        //当前行
        int curRowIndex = cell.getRowIndex();
        //当前列
        int curColIndex = cell.getColumnIndex();

        if (curRowIndex > mergeRowIndex) {
    
    
            for (int i = 0; i < mergeColumnIndex.length; i++) {
    
    
                if (curColIndex == mergeColumnIndex[i]) {
    
    
                    mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
                    break;
                }
            }
        }
    }

    private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
    
    
        //获取当前行的当前列的数据和上一行的当前列列数据,通过上一行数据是否相同进行合并
        Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() :
                cell.getNumericCellValue();
        Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
        Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() :
                preCell.getNumericCellValue();
        // 比较当前行的第一列的单元格与上一行是否相同,相同合并当前单元格与上一行
        if (curData.equals(preData)) {
    
    
            Sheet sheet = writeSheetHolder.getSheet();
            List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
            boolean isMerged = false;
            for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
    
    
                CellRangeAddress cellRangeAddr = mergeRegions.get(i);
                // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
                if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
    
    
                    sheet.removeMergedRegion(i);
                    cellRangeAddr.setLastRow(curRowIndex);
                    sheet.addMergedRegion(cellRangeAddr);
                    isMerged = true;
                }
            }
            // 若上一个单元格未被合并,则新增合并单元
            if (!isMerged) {
    
    
                CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex,
                        curColIndex);
                sheet.addMergedRegion(cellRangeAddress);
            }
        }
    }

    public int[] getMergeColumnIndex() {
    
    
        return mergeColumnIndex;
    }

    public void setMergeColumnIndex(int[] mergeColumnIndex) {
    
    
        this.mergeColumnIndex = mergeColumnIndex;
    }

    public int getMergeRowIndex() {
    
    
        return mergeRowIndex;
    }

    public void setMergeRowIndex(int mergeRowIndex) {
    
    
        this.mergeRowIndex = mergeRowIndex;
    }
}

Guess you like

Origin blog.csdn.net/qq_40042416/article/details/130305963