Java使用EasyExcel根据模板导出excel

EasyExcel

Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。

easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便。官方网站

16M内存23秒读取75M(46W行25列)的Excel(3.2.1+版本)

Java使用EasyExcel根据模板导出excel_第1张图片

导入依赖

<dependency>
    <groupId>com.alibabagroupId>
    <artifactId>easyexcelartifactId>
    <version>3.2.1version>
dependency>

模板文件

模板文件里面的变量对应实体类的属性,{.aaa}这种类型的是集合,{aaa}这种类型的是对象。

Java使用EasyExcel根据模板导出excel_第2张图片

代码示例

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

}

实体类

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

合并单元格工具类

合并单元格前提条件,数据一样。上面我写的测试数据,其中有三个单元格数据是一样的。

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

你可能感兴趣的:(excel,java,开发语言)