[Case combat] SpringBoot integrates EasyExcel to realize the list export function

This article will give you a practical operation of the function of exporting a list into an excel table. I believe that you will also encounter the need for exporting a list in your daily work. After reading this article, you can easily integrate the function of exporting a list .

insert image description here

The export tool used this time is Alibaba’s open source EasyExcel. Here is a brief introduction about EasyExcel:

We know that the more famous 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, such as Excel version 07 decompression and storage after decompression. It is done in memory, and the memory consumption is still very large.

easyexcel rewrote poi's analysis of Excel version 07, a 3M excel with POI sax analysis still needs about 100M 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.

insert image description here

Ok, let's start to integrate EasyExcel to realize the export function.

The first step: We first create a Maven project and integrate it into a SpringBoot project.

  • Add dependencies, create test interfaces, and ensure that the project can run.
  • pom.xml join dependency
		<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
        <relativePath/>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>
    </dependencies>
  • define the main class
/**
 * @author lixiang
 * @date 2023/5/26 16:02
 */
@SpringBootApplication
public class ExcelApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(ExcelApplication.class, args);
    }
}
  • create controller
/**
 * @author lixiang
 * @date 2023/5/26 16:00
 */
@RestController
@RequestMapping("/excel")
public class ExcelController {
    
    

    @GetMapping("/create")
    public String create(){
    
    
        String msg = "SUCCESS";
        return msg;
    }

}
  • test run

insert image description here

Step 2: Introduce the EasyExcel dependency and encapsulate the tool class

        <dependency>
            <groupId>com.pig4cloud.excel</groupId>
            <artifactId>excel-spring-boot-starter</artifactId>
            <version>0.5.0</version>
        </dependency>
package com.lixiang.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.handler.RowWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
 * 导出excel 表格 工具类
 * @author lixiang
 * @date 2023/5/26 10:18 
 */
public class EasyExcelUtil implements RowWriteHandler {
    
    

    private int mergeRowIndex;//从哪一行开始合并
    private int[] mergeColumnIndex;//excel合并的列
    private int[] signNum;//合并的唯一标识
    private int total;//总行数
    private int lastRow;
    private int firstCol;
    private int lastCol;
    private int firstRow;
    private int mergeCount = 1;

    private EasyExcelUtil(){
    
    }

    private EasyExcelUtil(int mergeRowIndex, int[] mergeColumnIndex, int[] signNum, int total) {
    
    
        this.mergeRowIndex = mergeRowIndex;
        this.mergeColumnIndex = mergeColumnIndex;
        this.signNum = signNum;
        this.total = total;
    }

    /**
     * 导出excel
     * @param response
     * @param fileName 文件名称
     * @param exportList 导出数据
     * @param clazz 导出实体bean class对象
     * @param <T>
     * @throws IOException
     */
    public static <T> void createExcel(HttpServletResponse response, String fileName, List<T> exportList,
      Class<T> clazz)
      throws IOException {
    
    
        createExcel(response,fileName,exportList,clazz,null);
    }

    /**
     * 导出excel
     * @param response
     * @param fileName 文件名称
     * @param exportList 导出数据
     * @param clazz 导出实体bean class对象
     * @param cellMerge 单元格合并规则
     * @param <T>
     * @throws IOException
     */
    public static <T> void createExcel(HttpServletResponse response, String fileName, List<T> exportList,
      Class<T> clazz, CellMerge cellMerge)
      throws IOException {
    
    
        // 设置下载信息
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        fileName = URLEncoder.encode(fileName, "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");

        //定义ExcelWriterSheetBuilder
        ExcelWriterSheetBuilder excelWriterSheetBuilder = EasyExcel
          .write(response.getOutputStream(), clazz)
          .sheet(fileName);

        //合并单元格
        if (cellMerge != null) {
    
    
            // 从那一行开始合并
            int mergeRowIndex = 1;
            EasyExcelUtil
              excelMergeRowByRowStrategy = new EasyExcelUtil(mergeRowIndex, cellMerge.getMergeColumIndex(),
              cellMerge.getMergeRuleColumIndex(), exportList.size());
            excelWriterSheetBuilder.registerWriteHandler(excelMergeRowByRowStrategy);
        }

        //设置头样式
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        //设置内容格式
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        HorizontalCellStyleStrategy horizontalCellStyleStrategy =
          new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
        //设计内容居中
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        //设置内容自动换行
        contentWriteCellStyle.setWrapped(true);
        excelWriterSheetBuilder.registerWriteHandler(horizontalCellStyleStrategy);

        //调用doWrite方法
        excelWriterSheetBuilder.doWrite(exportList);
    }

    @Override
    public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer integer, Integer integer1, Boolean aBoolean) {
    
    

    }

    @Override
    public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer integer, Boolean aBoolean) {
    
    

    }

    @Override
    public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
      Integer relativeRowIndex, Boolean isHead) {
    
    
        //当前行
        int curRowIndex = row.getRowNum();
        //每一行的最大列数
        short lastCellNum = row.getLastCellNum();

        if (curRowIndex == 1) {
    
    
            //赋初值 第一行
            firstRow = curRowIndex;
        }
        //开始合并位置
        if (curRowIndex > mergeRowIndex && !row.getCell(0).getStringCellValue().equals("")) {
    
    
            for (int i = 0; i < lastCellNum; i++) {
    
    
                if (i == mergeColumnIndex[i]) {
    
    
                    //当前行号 当前行对象 合并的标识位
                    mergeWithPrevAnyRow(writeSheetHolder.getSheet(), curRowIndex, row, signNum);
                    break;//已经进入到合并单元格操作里面了,执行一次就行
                }
            }
        }
    }

    public void mergeWithPrevAnyRow(Sheet sheet, int curRowIndex, Row row, int[] signNum) {
    
    
        Row preRow = row.getSheet().getRow(curRowIndex - 1);
        List<String> rowDataList = new ArrayList<>();
        List<String> preDataList = new ArrayList<>();

        for (int i : signNum) {
    
    
            Object currentData =
              row.getCell(i).getCellTypeEnum() == CellType.STRING ? row.getCell(i).getStringCellValue() :
                row.getCell(i).getNumericCellValue();
            Object preData =
              preRow.getCell(i).getCellTypeEnum() == CellType.STRING ? preRow.getCell(i).getStringCellValue() :
                preRow.getCell(i).getNumericCellValue();
            rowDataList.add(String.valueOf(currentData));
            preDataList.add(String.valueOf(preData));
        }

        String rowDataStr = String.join(",", rowDataList);
        String preDataStr = String.join(",", preDataList);

        //判断是否合并单元格
        boolean curEqualsPre = rowDataStr.equals(preDataStr);
        //判断前一个和后一个相同 并且 标识位相同
        if (curEqualsPre) {
    
    
            lastRow = curRowIndex;
            mergeCount++;
        }
        //excel过程中合并
        if (!curEqualsPre && mergeCount > 1) {
    
    
            mergeSheet(firstRow, lastRow, mergeColumnIndex, sheet);
            mergeCount = 1;
        }

        //excel结尾处合并
        if (mergeCount > 1 && total == curRowIndex) {
    
    
            mergeSheet(firstRow, lastRow, mergeColumnIndex, sheet);
            mergeCount = 1;
        }

        if (!curEqualsPre) {
    
    
            firstRow = curRowIndex;
        }

    }

    private void mergeSheet(int firstRow, int lastRow, int[] mergeColumnIndex, Sheet sheet) {
    
    
        for (int colNum : mergeColumnIndex) {
    
    
            firstCol = colNum;
            lastCol = colNum;
            CellRangeAddress cellRangeAddress = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol);
            sheet.addMergedRegion(cellRangeAddress);
        }
    }

    /**
     * 单元格合并类
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class CellMerge {
    
    
        private int[] mergeColumIndex;
        private int[] mergeRuleColumIndex;
    }

    /**
     * 设置单元格合并规则
     * @param mergeColumIndex
     * @param mergeRuleColumIndex
     * @return
     */
    public static CellMerge setCellMerge(int[] mergeColumIndex,int[] mergeRuleColumIndex){
    
    
        EasyExcelUtil excelUtil = new EasyExcelUtil();
        CellMerge cellMerge = excelUtil.new CellMerge();
        cellMerge.setMergeColumIndex(mergeColumIndex);
        cellMerge.setMergeRuleColumIndex(mergeRuleColumIndex);
        return cellMerge;
    }
}

Step 3: Simulate test data

  • Create student entity class

/**
 * @ColumnWidth(20) 这个是设置单元格长度的
 * @ExcelProperty("")  这个是设置表格头部的
 * @author lixiang
 * @date 2023/5/26 16:30
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@ColumnWidth(20)
public class Student {
    
    

    @ExcelProperty("年级")
    private String gradeName;

    @ExcelProperty("班级")
    private String className;

    @ExcelProperty("姓名")
    private String name;

    @ExcelProperty("年龄")
    private Integer age;

    @ExcelProperty("性别")
    private String sex;

}

  • mock student data
/**
 * @author lixiang
 * @date 2023/5/26 16:13
 */
@Service
public class StudentService {
    
    

    List<String> classNames;

    List<String> gradeNames;

    {
    
    
        classNames = new ArrayList<>();
        classNames.add("一班");
        classNames.add("二班");
        classNames.add("三班");
        gradeNames = new ArrayList<>();
        gradeNames.add("2017级");
        gradeNames.add("2018级");
        gradeNames.add("2019级");
    }

    public List<Student> getStudentData(){
    
    
        List<Student> list = new ArrayList<>();
        for (int i = 1; i < 20; i++) {
    
    
            Student student = new Student();
            student.setName("李祥"+i);
            student.setClassName(getClassName());
            student.setGradeName(getGradeName());
            student.setSex("男");
            student.setAge(18);
            list.add(student);
        }

        return list;
    }

    /**
     * 获取班级
     * @return
     */
    private String getClassName(){
    
    
        Random rand = new Random();
        return classNames.get(rand.nextInt(classNames.size()));
    }

    /**
     * 获取年级
     * @return
     */
    private String getGradeName(){
    
    
        Random rand = new Random();
        return gradeNames.get(rand.nextInt(gradeNames.size()));
    }
}
/**
 * @author lixiang
 * @date 2023/5/26 16:00
 */
@RestController
@RequestMapping("/excel")
public class ExcelController {
    
    

    @Autowired
    private StudentService  studentService;

    @GetMapping("/create")
    public List<Student> create(){
    
    

        List<Student> studentData = studentService.getStudentData();

        return  studentData;
    }

}

insert image description here

Step 4: Based on this data, we export it to excel

/**
 * @author lixiang
 * @date 2023/5/26 16:00
 */
@RestController
@RequestMapping("/excel")
public class ExcelController {
    
    

    @Autowired
    private StudentService  studentService;

    @GetMapping("/create")
    public void create(HttpServletResponse response) throws Exception{
    
    

        List<Student> studentData = studentService.getStudentData();
        String fileName  =  "学生列表";
        /**
         * 第一个参数:HttpServletResponse
         * 第二个参数:文件名称
         * 第三个参数:数据集
         * 第四个参数:数据集实体class对象
         */
        EasyExcelUtil.createExcel(response,fileName,studentData, Student.class);
    }

}

insert image description here
insert image description here

Ok, so now we want to merge cells, and merge the cells of the same class in the same grade.

    @GetMapping("/create")
    public void create(HttpServletResponse response) throws Exception{
    
    

        List<Student> studentData = studentService.getStudentData();
        String fileName  =  "学生列表";
        /**
         * 第一个参数:HttpServletResponse
         * 第二个参数:文件名称
         * 第三个参数:数据集
         * 第四个参数:数据集实体class对象
         * 第五个参数:合并单元格的规则
         *  EasyExcelUtil.setCellMerge(meargeColl, meargeColl);
         *  第一个参数 是 我们要合并的那些列,第二个是合并的规则。
         */
        int[] meargeColl = new int[]{
    
    0,1};
        EasyExcelUtil.CellMerge cellMerge = EasyExcelUtil.setCellMerge(meargeColl, meargeColl);
        EasyExcelUtil.createExcel(response,fileName,studentData, Student.class,cellMerge);
    }

insert image description here

Note that when merging, the data needs to be sorted to ensure that the data of the same grade and class are all together. Now we sort the data first.

  • Define Chinese sorting tool
/**
 * @author lixiang
 * @date 2023/5/26 17:14
 */
public class ChineseNumberUtil {
    
    
    public static int chineseNumber2Int(String chineseNumber){
    
    
        int result = 0;
        int temp = 1;//存放一个单位的数字如:十万
        int count = 0;//判断是否有chArr
        char[] cnArr = new char[]{
    
    '一','二','三','四','五','六','七','八','九'};
        char[] chArr = new char[]{
    
    '十','百','千','万','亿'};
        for (int i = 0; i < chineseNumber.length(); i++) {
    
    
            boolean b = true;//判断是否是chArr
            char c = chineseNumber.charAt(i);
            for (int j = 0; j < cnArr.length; j++) {
    
    //非单位,即数字
                if (c == cnArr[j]) {
    
    
                    if(0 != count){
    
    //添加下一个单位之前,先把上一个单位值添加到结果中
                        result += temp;
                        temp = 1;
                        count = 0;
                    }
                    // 下标+1,就是对应的值
                    temp = j + 1;
                    b = false;
                    break;
                }
            }
            if(b){
    
    //单位{'十','百','千','万','亿'}
                for (int j = 0; j < chArr.length; j++) {
    
    
                    if (c == chArr[j]) {
    
    
                        switch (j) {
    
    
                            case 0:
                                temp *= 10;
                                break;
                            case 1:
                                temp *= 100;
                                break;
                            case 2:
                                temp *= 1000;
                                break;
                            case 3:
                                temp *= 10000;
                                break;
                            case 4:
                                temp *= 100000000;
                                break;
                            default:
                                break;
                        }
                        count++;
                    }
                }
            }
            if (i == chineseNumber.length() - 1) {
    
    //遍历到最后一个字符
                result += temp;
            }
        }
        return result;
    }
}
  • service to sort
public List<Student> getStudentData(){
    
    
        List<Student> list = new ArrayList<>();
        for (int i = 1; i < 20; i++) {
    
    
            Student student = new Student();
            student.setName("李祥"+i);
            student.setClassName(getClassName());
            student.setGradeName(getGradeName());
            student.setSex("男");
            student.setAge(18);
            list.add(student);
        }
  			//排序逻辑
        list.sort((s1,s2)->{
    
    
            Integer flag = Integer.parseInt(s1.getGradeName().substring(0,4)) - Integer.parseInt(s2.getGradeName().substring(0,4));
            if(flag == 0){
    
    
                return Integer.compare(ChineseNumberUtil.chineseNumber2Int(s1.getClassName().substring(0,2)),
                        ChineseNumberUtil.chineseNumber2Int(s2.getClassName().substring(0,2)));
            }
            return flag;
        });
        return list;
    }

insert image description here

OK, we can merge after sorting.

				int[] meargeColl = new int[]{
    
    0,1};
        EasyExcelUtil.CellMerge cellMerge = EasyExcelUtil.setCellMerge(meargeColl, meargeColl);
        EasyExcelUtil.createExcel(response,fileName,studentData, Student.class,cellMerge);

insert image description here

We see that the same rows have been merged, ok, we are done with the excel integration.

Guess you like

Origin blog.csdn.net/weixin_47533244/article/details/130891333