【案例实战】SpringBoot整合EasyExcel实现列表导出功能

这篇具有很好参考价值的文章主要介绍了【案例实战】SpringBoot整合EasyExcel实现列表导出功能。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

这篇文章会给大家实操一个关于列表导出成excel表格的功能,相信大家在日常工作中也会遇到列表导出的需求,看完本篇文章那么你就可以轻松的去整合列表导出的功能。

【案例实战】SpringBoot整合EasyExcel实现列表导出功能

本次使用的导出工具是阿里巴巴开源的EasyExcel,关于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模式,在上层做了模型转换的封装,让使用者更加简单方便。

【案例实战】SpringBoot整合EasyExcel实现列表导出功能

ok,下面我们就开始实操整合一下EasyExcel,实现导出功能。

第一步:我们先创建Maven项目,整合成SpringBoot的项目。

  • 加入依赖,创建测试接口,确保项目能够运行。
  • pom.xml加入依赖
		<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>
  • 定义主类
/**
 * @author lixiang
 * @date 2023/5/26 16:02
 */
@SpringBootApplication
public class ExcelApplication {
    public static void main(String[] args) {
        SpringApplication.run(ExcelApplication.class, args);
    }
}
  • 创建控制器
/**
 * @author lixiang
 * @date 2023/5/26 16:00
 */
@RestController
@RequestMapping("/excel")
public class ExcelController {

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

}
  • 测试运行

【案例实战】SpringBoot整合EasyExcel实现列表导出功能

第二步:引入EasyExcel依赖,封装工具类

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

第三步:模拟测试数据

  • 创建学生实体类

/**
 * @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;

}

  • 模拟学生数据
/**
 * @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;
    }

}

【案例实战】SpringBoot整合EasyExcel实现列表导出功能

第四步:基于这个数据我们进行导出成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);
    }

}

【案例实战】SpringBoot整合EasyExcel实现列表导出功能
【案例实战】SpringBoot整合EasyExcel实现列表导出功能

Ok,那么现在 我们想要合并单元格,将同年级的同班级的单元格进行合并。

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

【案例实战】SpringBoot整合EasyExcel实现列表导出功能

注意合并的时候,需要将数据进行排序,确保年级,班级相同的数据都在一起。现在我们先进行数据排序。

  • 定义中文排序工具
/**
 * @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进行排序
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;
    }

【案例实战】SpringBoot整合EasyExcel实现列表导出功能

OK,我们排序完成就可以进行合并啦。

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

【案例实战】SpringBoot整合EasyExcel实现列表导出功能

我们看到相同的行已经被合并啦,ok,excel整合我们就完成啦。文章来源地址https://www.toymoban.com/news/detail-474392.html

到了这里,关于【案例实战】SpringBoot整合EasyExcel实现列表导出功能的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【案例实战】SpringBoot整合Redis实现缓存分页数据查询

    正式观看本文之前,设想一个问题,高并发情况下,首页列表数据怎么做? 类似淘宝首页,这些商品是从数据库中查出来的吗?答案肯定不是,在高并发的情况下,数据库是扛不住的,那么我们要怎么去扛住C端大并发量呢,这块我们可以借助Redis,我们知道Redis是一个基于内

    2024年02月09日
    浏览(58)
  • springboot中使用EasyExcel实现Excel 导入导出

    EasyExcel 是一款基于 Java 的简单易用的 Excel 文件操作工具。它提供了丰富的 API,可以方便地读取、写入和操作 Excel 文件,支持常见的 Excel 操作,如读取/写入单元格数据、合并单元格、设置样式、处理大数据量等。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将

    2024年02月12日
    浏览(67)
  • SpringBoot 集成 EasyExcel 3.x 实现 Excel 导出

    目录   EasyExcel官方文档 EasyExcel是什么? EasyExcel注解  springboot集成EasyExcel 简单入门导出 : 实体类  自定义转换类 测试一下 复杂表头一对多导出 :   自定义注解 定义实体类 自定义单元格合并策略  测试一下     EasyExcel官方文档 EasyExcel官方文档 - 基于Java的Excel处理工具

    2024年02月13日
    浏览(56)
  • 【EasyExcel】在SpringBoot+VUE项目中引入EasyExcel实现对数据的导出(封装工具类)

    一、引入EasyExcel 通过maven引入,坐标如下: 二、后端代码演示 下面以权限系统中的角色列表为案例,演示如何导出数据 实体类 工具类 通过@Component把工具类交给spring管理,在需要使用的地方使用@Resource注入即可 将泛型设置为 \\\" ? \\\",来表示任意类型,可以通过这一个方法完成

    2024年02月16日
    浏览(56)
  • springboot集成pagehelper以及easyExcel实现百万数据导出

    1.编写excel常量类 2.根据数据量的不同,我们选择不同的导出方法 数据量少的(20W以内吧):一个SHEET一次查询导出 数据量适中(100W以内):一个SHEET分批查询导出  数据里很大(几百万都行):多个SHEET分批查询导出 少执行sql,优化分页速度,才能更快的导出文件

    2023年04月15日
    浏览(46)
  • SpringBoot 集成 EasyExcel 3.x 优雅实现 Excel 导入导出

    EasyExcel 是一个基于 Java 的、快速、简洁、解决大文件内存溢出的 Excel 处理工具。它能让你在不用考虑性能、内存的等因素的情况下,快速完成 Excel 的读、写等功能。 EasyExcel文档地址: https://easyexcel.opensource.alibaba.com/ 引入依赖 简单导出 以导出用户信息为例,接下来手把手教

    2024年02月15日
    浏览(47)
  • Springboot基于easyexcel实现一个excel文件包含多个sheet表格的数据导出

    EasyExcel 是一款基于Java的开源Excel操作工具,它提供了简单且强大的 API,使开发人员可以轻松地读写、操作和生成Excel文件。 EasyExcel 支持 Excel 文件的导入和导出,可以处理大量数据,具有高性能和低内存占用。它可以读取 Excel 文件中的数据,并将数据转换为 Java 对象,也可

    2024年02月03日
    浏览(59)
  • 基于SpringBoot + EasyExcel + Vue + Blob实现导出Excel文件的前后端完整过程

    首先前端发起HTTP请求之后,后端返回一个Excel输出流,然后前端用Blob类型接收数据,并且解析响应头数据以及提取源文件名,最后用a标签完成下载。 一、后端代码 (1)导入阿里巴巴的EasyExcel依赖(pom.xml) (2)控制层(GameController.java) (3)接口层(IGameService.java) (4)

    2024年02月16日
    浏览(43)
  • 【案例实战】SpringBoot整合阿里云文件上传OSS

    1.需求背景 C端业务用户头像上传 海量图片音频、视频存储 用户行为日志存储 (1)阿里云OSS介绍 对象存储OSS(Object Storage Service)是阿里云提供的海量、安全、低成本、高持久的云存储服务。其数据设计持久性不低于99.9999999999%(12个9),服务设计可用性不低于99.995%。 OSS具

    2024年02月06日
    浏览(55)
  • RabbitMq整合Springboot超全实战案例+图文演示+源码自取

    目录 介绍 简单整合 简单模式 定义 代码示例  work模式 定义 代码示例 pubsub模式 定义 代码示例 routing模式  定义 代码示例  top模式 定义 代码 下单付款加积分示例 介绍 代码  可靠性投递示例 介绍 代码 交换机投递确认回调  队列投递确认回调  ​延迟消息场景示例 介绍 代

    2024年02月03日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包