EasyExcel使用总结

EasyExcel

1、导入

1.1、基本方式导入

1.导入依赖

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.js</groupId>
    <artifactId>EasyExcel-Test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!--easy-excel-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.1.4</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--Junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>

        <!--slf4j接口-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.6</version>
        </dependency>
        <!--slf4j实现-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.6</version>
        </dependency>

        <!--fast-josn-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.22</version>
        </dependency>
    </dependencies>

</project>
2. 加载源文件

使用File 或 InputStream 的方式加载Excel 源文件

基本语法
//示例:从本地File文件导入。
File file = new File("Excel本地文件路径.xlsx");
List<Object>list = EasyExcel.read(file).sheet(0).doReadsync();

//示例:从InputStream输入流中导入。
InputStream inputStream = TestSample.class.getClassLoader(),aetResourceAsstream("Excel资源路径,xlsx");
List<Object>list = EasyExcel.read(inputStream).sheet(0).doReadsync();
3. 读取数据行

使用 EasyExcel 载入并读取源文件数据行内容

实例

在这里插入图片描述

package cn.js;

import com.alibaba.excel.EasyExcel;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.io.File;
import java.util.List;

/**
 * Description:
 *
 * @Author Js
 * @Create 2025-05-03 20:05
 * @Version 1.0
 */
@Slf4j
public class EasyExcelTest {

    @Test
    public void testFile(){
        File file = new File("C:\\Users\\lenovo\\Desktop\\123\\Easy-test.xlsx");
        List<Object> list = EasyExcel.read(file).sheet(0).doReadSync();
        for (Object item : list) {
            log.info("item:{}",item);
        }


    }
}

在这里插入图片描述


实例2

package cn.js;

import com.alibaba.excel.EasyExcel;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.io.File;
import java.io.InputStream;
import java.util.List;

/**
 * Description:
 *
 * @Author Js
 * @Create 2025-05-03 20:05
 * @Version 1.0
 */
@Slf4j
public class EasyExcelTest {

   /**
     * 我们将:Easy-test.xlsx 文件放到 resource 目录下面
     */
    @Test
    public void testInputStream(){
        InputStream inputStream = EasyExcelTest.class.getClassLoader().getResourceAsStream("Easy-test.xlsx");
        List<Object> list = EasyExcel.read(inputStream).sheet(0).doReadSync();
        for (Object item : list) {
            log.info("item:{}",item);
        }

    }


}

在这里插入图片描述

4. 读取结果

获取数据行条目及各行数据内容

在这里插入图片描述

在这里插入图片描述


1.2、模型映射导入

我们第一种导出结果,可以看出,是一个Map 的结构,我们在获取数据的时候,如果通过角标 Key获取值的话,很不方便的,而且每一个key 对应的Value 的类型也不一样。如果我们定义好一个类,将类中的属性和Excel 列进行一 一 绑定,这样既能知道value的类型,也更加明确的好获取每个数据。

1.定义实体映射类
package cn.js;

import lombok.Data;

import java.util.Date;

/**
 * Description:
 *
 * @Author Js
 * @Create 2025-05-03 20:34
 * @Version 1.0
 */
@Data
public class UserInfoModel {
    /**
     * 昵称
     */
    private String userName;

    /**
     * 性别
     */
    private String userGender;

    /**
     * 生日
     */
    @DateTimeFormat(value="yyyy年MM月dd日 HH时mm分ss秒")
    private Date userBirth;

    /**
     * 邮箱
     */
    private String userEmail;


    /**
     * 积分
     */
    private Integer userScore;

    /**
     * 排名
     */
   // private Double userRank;
    
    @NumberFormat(value="#.##%")
	private String userRank;

}

2. 操作读取
基本语法
关联映射 @ExcelProperty:
    将类的属性,和excel的单元格进行绑定,默认是按照顺序进行解析的,注意类的字段类型,要符合excel中的字段,否则会报错;
    建议使用“列名” 进行绑定
    
    @ExcelProperty(index=0)
    @ExcelProperty(value ="列名")
    
格式转换
    @DateTimeFormat(value="yyyy年MM月dd日 HH时mm分ss秒")
    @NumberFormat(value="#.##%")
    @ExcelProperty(converter=自定义格式转换.class)
    
    
相关方法
	EasyExcel.read(inputStream).head(映射模型.class).doReadSync();
3. 读取数据行
@Test
    public void testInputStreamWithModel() {
        InputStream inputStream = EasyExcelTest.class.getClassLoader().getResourceAsStream("Easy-test.xlsx");

        List<UserInfoModel> list = EasyExcel.read(inputStream).sheet(0).head(UserInfoModel.class).doReadSync();

        for (UserInfoModel item : list) {
            log.info("昵称:{},性别{},生日{},邮箱{},积分{},排名{}", item.getUserName(),item.getUserGender(),item.getUserBirth(),item.getUserEmail(),item.getUserScore(),item.getUserRank());
        }

    }
4. 读取结果

在这里插入图片描述

1.3、导入类型转换器

语法

实现 implements Converter<就是实际导进来需要转换成java中啥类型> 接口中convertToJavaData 方法

在需要转换的映射模型字段上面加上如下注解

@ExcelProperty(converter = 类型转换器.class)

类型转换器

package cn.js;

import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;

/**
 * Description:
 *
 * @Author Js
 * @Create 2025-05-03 21:04
 * @Version 1.0
 */

// Converter<Integer>:就是实际导进来需要转换成啥类型
public class UserInfoGenderConverter implements Converter<Integer> {


    /**
     * 导入时: 把excel中的数据转成java类型
     */
    @Override
    public Integer convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {

        String value = cellData.getStringValue();
        switch (value) {
            case "男":
                return 1;
            case "女":
                return 2;
            default:
                return 0;
        }


    }


    /**
     * 导出时: 把java中的数据转成符合excel类型
     */
    @Override
    public WriteCellData<?> convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        return Converter.super.convertToExcelData(value, contentProperty, globalConfiguration);
    }
}

使用类型转换器: @ExcelProperty(converter = 类型转换器所在的类.class),将这个注解贴在要转换的模型映射类中对应的Excel字段上面,即可。

​ 如:将excel中的男,女转换成:1,0

/**
 * 性别 
 */
@ExcelProperty(converter = UserInfoGenderConverter.class)//类型转换器
private Integer userGender;
//private String userGender;
package cn.js;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import lombok.Data;

import java.util.Date;

/**
 * Description:
 *
 * @Author Js
 * @Create 2025-05-03 20:34
 * @Version 1.0
 */
@Data
public class UserInfoModel {
    /**
     * 昵称
     */
    private String userName;

    /**
     * 性别
     */
    @ExcelProperty(converter = UserInfoGenderConverter.class)//类型转换器
    private Integer userGender;
    //private String userGender;

    /**
     * 生日
     */
    @DateTimeFormat(value="yyyy年MM月dd日 HH时mm分ss秒")
    private Date userBirth;

    /**
     * 邮箱
     */
    private String userEmail;


    /**
     * 积分
     */
    private Integer userScore;

    /**
     * 排名
     */
    @NumberFormat(value="#.##%")
    private String userRank;
    //private Double userRank;


}

测试

	 @Test
    public void testInputStreamWithModel() {
        InputStream inputStream = EasyExcelTest.class.getClassLoader().getResourceAsStream("Easy-test.xlsx");

        List<UserInfoModel> list = EasyExcel.read(inputStream).sheet(0).head(UserInfoModel.class).doReadSync();

        for (UserInfoModel item : list) {
            log.info("昵称:{}, 性别:{}, 生日:{}, 邮箱:{}, 积分:{}, 排名:{}", item.getUserName(),item.getUserGender(),item.getUserBirth(),item.getUserEmail(),item.getUserScore(),item.getUserRank());
        }

    }

在这里插入图片描述


1.4、导入监听器

为啥需要监听器?我们在读取excel 文件时候,不管是File 还是InputStream 流,都是直接使用.doReadSync() 方法直接读取,但是我们在实际当中,可能还得需要对Excel中数据进行:

1.数据格式校验

2.自动适配内容

3.解析异常处理

4.分批入库

5.节约内存 ……操作

基本语法:
//1.示例:使用匿名内部类。
EasyExcel.read(inputStream)
.head(UserInfoModel.class)
.registerReadListener(new ReadListener<映射模型>(){})
.sheet(0);

//2.示例:使用监听实现类。
public class 监听实现类 implements ReadListender<映射模型>{}

EasyExcel.read(inputStream)
.head(UserInfoModel.class)
.registerReadListener(new监听实现类())
.sheet(0);
    
//3.示例:简写方式。
EasyExcel.read(inputStream, UserinfoModel.class, new 监听实现类()).sheet(0);

使用步骤

package cn.js.listener;

import cn.js.model.UserInfoModel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import lombok.extern.slf4j.Slf4j;

/**
 * Description:
 *
 * @Author Js
 * @Create 2025-05-03 21:52
 * @Version 1.0
 */
@Slf4j
public class UserInfoReadListener implements ReadListener<UserInfoModel> {

    /**
     *  解析excel中每一条数据时,会执行的方法
     * @param userInfoModel
     * @param analysisContext
     */
    @Override
    public void invoke(UserInfoModel userInfoModel, AnalysisContext analysisContext) {
        log.info("解析每一行excel的 invoke 方法:{}" ,userInfoModel);
    }

    /**
     * 解析完 excel中所有数据时,会执行的方法
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        log.info("解析完excel的 doAfterAllAnalysed 方法:" );
    }
}

使用

	@Test
    public void testInputStreamWithListener() {
        InputStream inputStream = EasyExcelTest.class.getClassLoader().getResourceAsStream("Easy-test.xlsx");

        List<UserInfoModel> list = EasyExcel.read(inputStream)
                .sheet(0)
                .registerReadListener(new UserInfoReadListener())//监听器
                .head(UserInfoModel.class).doReadSync();

    }

在这里插入图片描述


1.5、多行表头导入

应用场景

一般在导入数据的时候我们会给用户一个模版,让用户按照模版要求进行导入。

比方说下图,前五行,是我们提供给用户的注意事项,从第六行开始,才是我们的excel数据的表头,此时我们应该指定,数据真正的表头,是第几行。

在这里插入图片描述

基本语法:
EasyExcel.read(inputStream)
.sheet(0)
.head(映射模型.class)
//关键代码:设置表头行数。
.headRowNumber(6)
.registerReadListener(new监听器实现类())
.""””..

在这里插入图片描述

测试

@Test
    public void testInputStreamWithModel1() {
        InputStream inputStream = EasyExcelTest.class.getClassLoader().getResourceAsStream("Easy-test1.xlsx");

        List<UserInfoModel> list = EasyExcel.read(inputStream).sheet(0).head(UserInfoModel.class)
            .headRowNumber(6)//关键代码:设置表头行数。
            .doReadSync();

        for (UserInfoModel item : list) {
            log.info("昵称:{}, 性别:{}, 生日:{}, 邮箱:{}, 积分:{}, 排名:{}", item.getUserName(),item.getUserGender(),item.getUserBirth(),item.getUserEmail(),item.getUserScore(),item.getUserRank());
        }

    }

在这里插入图片描述


1.6、导入异常处理

处理方式也是实现监听器,并重写其中的 onException 方法,里面的异常封装着数据错误信息。

public void onException(Exception exception, AnalysisContext context) throws Exception

基本语法:

/**
     * 解析异常时,会执行的方法
     * @param exception
     * @param context
     * @throws Exception
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
      //  ReadListener.super.onException(exception, context);
        ExcelDataConvertException dataConvertException = (ExcelDataConvertException) exception;
        //获取行的下标
        Integer rowIndex = dataConvertException.getRowIndex();
        //获取列的下标
        Integer columnIndex = dataConvertException.getColumnIndex();
        //数据
        String content = dataConvertException.getCellData().getStringValue();
        //原因
        String message = dataConvertException.getMessage();

        log.error("解析失败,请检查输入的第{}行第{}列数据是否正确:{}",rowIndex,columnIndex,message);
        log.warn("Excel data conversion error at row {}, column {}, content: {}", rowIndex, columnIndex, content);
        throw new ExcelAnalysisStopException();//必须抛出

    }

标准化处理

  1. 定义一个读取错误的Moder类
package cn.js.model;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.io.Serializable;

/**
* Description:
*
* @Author Js
* @Create 2025-05-03 22:57
* @Version 1.0
*/
@Data
@AllArgsConstructor
public class ReadErrorModel implements Serializable {
  private static final long serialVersionUID = 1L;

  /**
   * 错误信息
   */
  private String message;
  /**
   * 行号
   */
  private Integer rowIndex;

  /**
   * 列号
   */
  private Integer columnIndex;

  /**
   * 单元格数据
   */
  private String cellData;


}


  1. 测试

    package cn.js.listener;
    
    import cn.js.model.UserInfoModel;
    import com.alibaba.excel.context.AnalysisContext;
    import com.alibaba.excel.exception.ExcelDataConvertException;
    import com.alibaba.excel.read.listener.ReadListener;
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * Description:
     *
     * @Author Js
     * @Create 2025-05-03 21:52
     * @Version 1.0
     */
    @Slf4j
    public class UserInfoReadListener implements ReadListener<UserInfoModel> {
    
        /**
         *  解析excel中每一条数据时,会执行的方法
         * @param userInfoModel
         * @param analysisContext
         */
        @Override
        public void invoke(UserInfoModel userInfoModel, AnalysisContext analysisContext) {
            log.info("解析每一行excel的 invoke 方法:{}" ,userInfoModel);
        }
    
        /**
         * 解析完 excel中所有数据时,会执行的方法
         * @param analysisContext
         */
        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
            log.info("解析完excel的 doAfterAllAnalysed 方法:" );
        }
    
    
        /**
         * 解析异常时,会执行的方法
         *
         * @param exception
         * @param context
         * @throws Exception
         */
        @Override
        public void onException(Exception exception, AnalysisContext context) throws Exception {
            //  ReadListener.super.onException(exception, context);
            ExcelDataConvertException dataConvertException = (ExcelDataConvertException) exception;
            //获取行的下标
            Integer rowIndex = dataConvertException.getRowIndex();
            //获取列的下标
            Integer columnIndex = dataConvertException.getColumnIndex();
            //数据
            String content = dataConvertException.getCellData().getStringValue();
            //原因
            String message = dataConvertException.getMessage();
    
            this.readErrorModel = new ReadErrorModel(rowIndex, columnIndex, content, message);
            throw new ExcelAnalysisStopException();//必须抛出
    
    
        }
    }
    
    
  2. 运行

    @Test
        public void testInputStreamWithListener2() {
            InputStream inputStream = EasyExcelTest.class.getClassLoader().getResourceAsStream("Easy-test.xlsx");
            UserInfoReadListener listener = new UserInfoReadListener();
            List<UserInfoModel> list = EasyExcel.read(inputStream)
                    .sheet(0)
                    .registerReadListener(listener)//监听器
                    .head(UserInfoModel.class).doReadSync();
    
            if(listener.getReadErrorModel()!=null){
                log.info("读取失败:{}",listener.getReadErrorModel());
            }else {
                log.info("读取成功:{}",list);
            }
            }
    

在这里插入图片描述

在这里插入图片描述


1.7、文件上传导入

导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.js</groupId>
    <artifactId>EasyExcel-Test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!--easy-excel-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.2.1</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.commons</groupId>
                    <artifactId>commons-compress</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    <!--commons-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-compress</artifactId>
            <version>1.23.0</version>
        </dependency>

        <!--Junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>

        <!--slf4j接口-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.6</version>
        </dependency>
        <!--slf4j实现-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.6</version>
        </dependency>

        <!--fast-josn-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.22</version>
        </dependency>

        <!--springBoot-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.11</version>
            <exclusions>
                <exclusion>
                    <groupId>org.yaml</groupId>
                    <artifactId>snakeyaml</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>2.0</version>
        </dependency>
    </dependencies>

</project>
package cn.js.controller;

import cn.js.model.UserInfoModel;
import com.alibaba.excel.EasyExcel;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * Description:
 *
 * @Author Js
 * @Create 2025-05-03 23:18
 * @Version 1.0
 */
@RestController
@RequestMapping("/hom")
public class HomController {
    @RequestMapping("/test")
    public String test(){
        return "hello";
    }

    @PostMapping("/test2")
    public List<UserInfoModel> update(MultipartFile file) throws IOException {

        //获取:文件输入流
        InputStream inputStream = file.getInputStream();
        List<UserInfoModel> list = EasyExcel.read(inputStream).sheet(0)
            .head(UserInfoModel.class)
            .registerReadListener(new UserInfoReadListener())//监听器
            doReadSync();
        return list;
    }

}

在这里插入图片描述


2、导出

1、基本方式导出

基本语法
 EasyExcel.write("导出目标").sheet().doWrite("集合数据");
//导出目标:
		1.string 文件路径
        2.File 文件对象
        3.OutStream 输出流
        
//集合数据:
        1.List<List<Object>>
        2.List<Map<Integer,Object>>
		3.List<Object>

使用List集合导出

package cn.js;

import cn.js.model.UserInfoModel;
import com.alibaba.excel.EasyExcel;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;

/**
 * Description:
 *
 * @Author Js
 * @Create 2025-05-04 13:04
 * @Version 1.0
 */
@Slf4j
public class EasyWiteTest {

    private static final String FILE_NAME = "C:\\Users\\lenovo\\Desktop\\123\\test.xlsx";

    @Test
    public void  witeList(){
        List<Object> list1 = Arrays.asList(1, 2, 3);
        List<Object> list2 = Arrays.asList("郭德纲", "王德纲", "张德纲");
        List<List<Object>> list = Arrays.asList(list1, list2);
        EasyExcel.write(FILE_NAME).sheet("测试").doWrite(list);


    }
}

在这里插入图片描述


使用Map集合导出


    @Test
    public void  witeMap() throws ParseException {
        Map<Integer, Object> map1 = new HashMap<>();
        map1.put(0, "张三");
        map1.put(1, "男");
        map1.put(2, DateUtils.parseDate("2025-05-04"));
        map1.put(3, "zhangsan@163.com");

        HashMap<Integer, Object> map2 = new HashMap<>();
        map2.put(0, "李四");
        map2.put(1, "女");
        map2.put(2, DateUtils.parseDate("2025-05-04"));
        map2.put(3, "lisi@163.com");

        List<Map<Integer, Object>> list = Arrays.asList(map1, map2);


        EasyExcel.write(FILE_NAME).sheet("测试").doWrite(list);


    }

在这里插入图片描述


使用List对象集合导出

package cn.js.model;

import cn.js.converter.UserInfoGenderConverter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.Date;

/**
 * Description:
 *
 * @Author Js
 * @Create 2025-05-04 13:20
 * @Version 1.0
 */
@Data
@AllArgsConstructor
public class UserWriteModel {
    /**
     * 昵称
     */
    private String userName;

    /**
     * 性别
     */

    private String userGender;

    /**
     * 生日
     */
    @DateTimeFormat(value="yyyy年MM月dd日 HH时mm分ss秒")
    private Date userBirth;

    /**
     * 邮箱
     */
    private String userEmail;
}

@Test
    public void objectList() throws ParseException {
        UserWriteModel model1 = new UserWriteModel("张三", "男", DateUtils.parseDate("2025-05-04"), "zhangsan@163.com");
        UserWriteModel model2 = new UserWriteModel("李四", "女", DateUtils.parseDate("2025-05-04"), "lisi@163.com");
        List<UserWriteModel> list = Arrays.asList(model1, model2);
        EasyExcel.write(FILE_NAME).sheet("测试").doWrite(list);
    }

在这里插入图片描述


2、模型映射导出

其一步实我们,在上一步中ListObject 使用过程当中就已经使用过了,但是可以发现导出的时候Excel 中是没有表头的,而且数据没有格式化……等操作

基本语法
// 关联映射&设置标题
    @ExcelProperty(index=0)//表示:数据放在excel中那一列
    @ExcelProperty(value ="列名")
    @ExcelProperty(value ={"主标题""副标题"})

//显示隐藏
	@Excellgnore//在那个模型映射字段添加该注解,这个字段在导出的,就会忽略掉
    //设置那些模型字段导出
    EasyExcel.write(导出目标)
        .head(映射模型.class)
        .excludeColumnFiledNames(Collection<String>)//根据集合设置,那些列不导出,Collection<String>:那些模型映射类的字段,:userName,userGender,将这些封装成Lsit集合。
        .includeColumnFiledNames(Collection<String>)//根据集合设置,那些列导出
     .doWrite(集合数据);     

//格式转换
    @DateTimeFormat(value="yyyy年MM月dd日")
    @NumberFormat(value="#.##%")
    @ExcelProperty(converter=自定义转换.class)

// 相关方法
	EasyExcel.write(导出目标).head(映射模型.class).doWrite(集合数据);


关联映射&设置标题

package cn.js.model;


import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.math.BigDecimal;
import java.util.Date;

/**
 * Description:
 *
 * @Author Js
 * @Create 2025-05-04 13:20
 * @Version 1.0
 */
@Data
@AllArgsConstructor
public class UserWriteModel {
    /**
     * 昵称
     */
    @ExcelProperty(value = {"基本信息", "姓名"})
    private String userName;

    /**
     * 性别
     */
    @ExcelProperty(value = {"基本信息", "性别"})
    private Integer userGender;

    /**
     * 生日
     */
    @ExcelProperty(value = {"基本信息", "生日"})
    @DateTimeFormat(value = "yyyy年MM月dd日 HH时mm分ss秒")
    private Date userBirth;

    /**
     * 邮箱
     */
    @ExcelProperty(value = {"基本信息", "邮箱"})
    private String userEmail;

    /**
     * 积分
     */
    @ExcelProperty(value = {"账户信息", "积分"})
    private Integer userScore;


    @ExcelProperty(value = {"账户信息", "奖励"})
    private BigDecimal userReward;
}

@Test
    public void objectList2() throws ParseException {
        UserWriteModel model1 = new UserWriteModel("张三疯", 1, DateUtils.parseDate("2025-05-04"), "zhangsan@163.com", 123, BigDecimal.valueOf(123.456));
        UserWriteModel model2 = new UserWriteModel("李四军", 0, DateUtils.parseDate("2025-05-04"), "lisi@163.com", 789, BigDecimal.valueOf(789.258));
        List<UserWriteModel> list = Arrays.asList(model1, model2);
        EasyExcel.write(FILE_NAME).sheet("测试").head(UserWriteModel.class).doWrite(list);
    }

在这里插入图片描述

3、自定义导出格式转换

语法

实现 implements Converter<就是实际导进来需要转换成java中啥类型> 接口中convertToExcelData方法

在需要转换的映射模型字段上面加上如下注解:

@ExcelProperty(converter = 类型转换器.class)

类型转换器

package cn.js;

import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;

/**
 * Description:
 *
 * @Author Js
 * @Create 2025-05-03 21:04
 * @Version 1.0
 */

// Converter<Integer>:就是实际导进来需要转换成啥类型
public class UserInfoGenderConverter implements Converter<Integer> {


    /**
     * 导入时: 把excel中的数据转成java类型
     */
    @Override
    public Integer convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {

        String value = cellData.getStringValue();
        switch (value) {
            case "男":
                return 1;
            case "女":
                return 2;
            default:
                return 0;
        }


    }


    /**
     * 导出时: 把java中的数据转成符合excel类型
     */
    @Override
    public WriteCellData<?> convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        switch (value) {
            case 1:
                return new WriteCellData<>("男");
            case 2:
                return new WriteCellData<>("女");
            default:
                return new WriteCellData<>("未知");
        }
    }
}

测试

模型映射类添加类型转换器

/**
* 性别
*/
@ExcelProperty(value = {"基本信息", "性别"},converter = UserInfoGenderConverter.class)
private Integer userGender;

1

@Test
   public void objectList2() throws ParseException {
       UserWriteModel model1 = new UserWriteModel("张三疯", 1, DateUtils.parseDate("2025-05-04"), "zhangsan@163.com", 123, BigDecimal.valueOf(123.456));
       UserWriteModel model2 = new UserWriteModel("李四军", 0, DateUtils.parseDate("2025-05-04"), "lisi@163.com", 789, BigDecimal.valueOf(789.258));
       List<UserWriteModel> list = Arrays.asList(model1, model2);
       EasyExcel.write(FILE_NAME).sheet("测试").head(UserWriteModel.class).doWrite(list);
   }

在这里插入图片描述

4、排除那些列导出

语法
  1. 直接在模型映射类的字段上面添加注解
//显示隐藏
@Excellgnore//在那个模型映射字段添加该注解,这个字段在导出的,就会忽略掉
  1. 在导出的时候设置那些是否导出

      //设置那些模型字段导出
        EasyExcel.write(导出目标)
            .head(映射模型.class)
            //根据集合设置,那些列不导出,Collection<String>:那些模型映射类的字段,如:userName,userGender,将这些封装成Lsit集合。
            .excludeColumnFiledNames(Collection<String>)
            //根据集合设置,那些列导出
            .includeColumnFiledNames(Collection<String>)
         .doWrite(集合数据);     
    

5、导出行高列宽

语法:
  1. 手动设置,通过在模型映射类的字段上面添加注解,或者在模型类上面添加,在模型类上面添加,所有导出的字段都生效。

    //行高(内容):
    	@ContentRowHeight(value=10)
    //行高(标题):
    	@HeadRowHeight(value=20)
    //列宽:
    	@ColumnWidth(value=25)
    
  2. 自动列宽,也可以参照 LongestMatchColumnWidthStyleStrategy 类重写一个

    EasyExcel.write(file)
        //自动列宽:根据内容长度自动匹配:默认按照标题匹配
    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
        .sheet()
    .doWrite(data);
    

6、合并单元格导出

语法
  1. 使用注解:

    //firstRowIndex:开始第几行,lastRowindex结束是第几行,firstcolumnindex:第几列开始,lastColumnIndex:第几列结束
    @OnceAbsoluteMerge(firstRowIndex =2,lastRowindex =3, firstcolumnindex=0,lastColumnIndex=0)
    public class 模型映射类{}
    
    @ContentLoopMerge(eachRow=2)
    private String fieldName;
    
    1. 自定义策略:
    // 循环合并:
    	new LoopMergeStrategy(eachRow:2,columnIndex:0);
    
    // 合并一次: 
    	new OnceAbsoluteMergestrategy(firstRowindex: 0, lastRowindex: 1, firstcolumnindex: 4, lastcolumnindex: 4);
    
    // 使用策略:
    EasyExcel.write(file)
        .head(映射模型.class)
    	.sheet()
    	.registerWriteHandler(合并策略)
        .doWrite(数据);
    

测试

  @Test
    public void objectList3() throws ParseException {
        UserWriteModel model1 = new UserWriteModel("第一团队","张三疯", 1, DateUtils.parseDate("2025-05-04"), "zhangsan@163.com", 123, BigDecimal.valueOf(123.456));
        UserWriteModel model2 = new UserWriteModel("第一团队","李四军", 0, DateUtils.parseDate("2025-05-05"), "lisi@163.com", 789, BigDecimal.valueOf(789.258));
        UserWriteModel model3 = new UserWriteModel("第二团队", "王五", 1, DateUtils.parseDate("2025-05-06"), "zhangsxxn@163.com", 124, BigDecimal.valueOf(123.056));
        UserWriteModel model4 = new UserWriteModel("第二团队", "赵六", 1, DateUtils.parseDate("2025-05-07"), "zhdsfgxxn@163.com", 604, BigDecimal.valueOf(435.010));
        UserWriteModel model5 = new UserWriteModel("第三团队", "孙七", 0, DateUtils.parseDate("2025-05-08"), "zhdsfgxxn@163.com", 604, BigDecimal.valueOf(405.231));



        List<UserWriteModel> list = Arrays.asList(model1, model2, model3, model4, model5);
        EasyExcel.write(FILE_NAME).sheet("测试").head(UserWriteModel.class)
               // .registerWriteHandler(new OnceAbsoluteMergeStrategy(2,3,0,0))
                .registerWriteHandler(new LoopMergeStrategy(2,0))
                .doWrite(list);
    }

在这里插入图片描述

自定义合并单元格
  1. 合并单元格,有两种方式:

    1. 是实现RowWriteHandler接口,这个是行合并,也是默认的
    2. 是实现CellWriteHandler接口,这个是列合并
  2. 实现 CellWriteHandler 接口

    package cn.js.converter;
    
    import com.alibaba.excel.metadata.Head;
    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 java.io.Serializable;
    import java.util.List;
    
    /**
     * Description:
     *
     * @Author Js
     * @Create 2025-05-04 14:54
     * @Version 1.0
     */
    public class CustomMergeStrategy implements CellWriteHandler {
    
        @Override
        public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
    
            // 如果是表头,不做处理
            if(isHead) {
               return;
           }
            //relativeRowIndex: 当前行号,如果当前是第一行,则不做处理
            if(relativeRowIndex== 0) {
                return;
            }
            //获取当前表格,当前行下标,上一行下标,上一行对象,上一列对象
            Sheet sheet = cell.getSheet();
            int rowIndexCurrent = cell.getRowIndex();
            int lastRowIndex = rowIndexCurrent - 1;
            Row rowPrev = sheet.getRow(lastRowIndex);
            Cell cellPrev = rowPrev.getCell(cell.getColumnIndex());
    
            //获取当前单元格的值
            Object cellValueCurrent = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
            //获取上一单元格的值
            Object cellValuePrev = cellPrev.getCellType() == CellType.STRING ? cellPrev.getStringCellValue() : cellPrev.getNumericCellValue();
    
            //如果当前单元格的值和上一单元格的值相同,则合并单元格
            if(cellValueCurrent.equals(cellValuePrev)) {
                Boolean isMerged = false;
                //获取已有的合并区域
                for(int i = 0;i<sheet.getNumMergedRegions();i++) {
                    org.apache.poi.ss.util.CellRangeAddress range = sheet.getMergedRegion(i);
                    if(range.isInRange(rowIndexCurrent, cell.getColumnIndex())){
                        //如果当前单元格已经在合并区域中,则先移除合并区域
                        sheet.removeMergedRegion(i);
                        //重新合并单元格的结束行
                        range.setLastRow(rowIndexCurrent);
                        //重新添加合并区域
                        sheet.addMergedRegion(range);
                        isMerged = true;
                        break;
                    }
                    }
                if(!isMerged) {
                    //如果当前单元格不在合并区域中,则直接添加新的合并区域
                    sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(lastRowIndex, rowIndexCurrent, cellPrev.getColumnIndex(), cellPrev.getColumnIndex()));
                }
    
            }
        }
    }
    
    

测试

  1. 模型类
package cn.js.model;


import cn.js.converter.UserInfoGenderConverter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.math.BigDecimal;
import java.util.Date;

/**
 * Description:
 *
 * @Author Js
 * @Create 2025-05-04 13:20
 * @Version 1.0
 */
@Data
@AllArgsConstructor
public class UserWriteModel {

    /**
     * 团队名称
     */
    @ExcelProperty(value =  "团队名称")
    private String teamName;

    /**
     * 昵称
     */
    @ExcelProperty(value = {"基本信息", "姓名"})
    private String userName;

    /**
     * 性别
     */
    @ExcelProperty(value = {"基本信息", "性别"},converter = UserInfoGenderConverter.class)
    private Integer userGender;

    /**
     * 生日
     */
    @ExcelProperty(value = {"基本信息", "生日"})
    @DateTimeFormat(value = "yyyy年MM月dd日 HH时mm分ss秒")
    private Date userBirth;

    /**
     * 邮箱
     */
    @ExcelProperty(value = {"基本信息", "邮箱"})
    private String userEmail;

    /**
     * 积分
     */
    @ExcelProperty(value = {"账户信息", "积分"})
    private Integer userScore;


    @ExcelProperty(value = {"账户信息", "奖励"})
    private BigDecimal userReward;
}

  1. 测试类
 @Test
    public void objectList3() throws ParseException {
        UserWriteModel model1 = new UserWriteModel("第一团队","张三疯", 1, DateUtils.parseDate("2025-05-04"), "zhangsan@163.com", 123, BigDecimal.valueOf(123.456));
        UserWriteModel model2 = new UserWriteModel("第一团队","李四军", 0, DateUtils.parseDate("2025-05-05"), "lisi@163.com", 789, BigDecimal.valueOf(789.258));
        UserWriteModel model3 = new UserWriteModel("第二团队", "王五", 1, DateUtils.parseDate("2025-05-06"), "zhangsxxn@163.com", 124, BigDecimal.valueOf(123.056));
        UserWriteModel model4 = new UserWriteModel("第二团队", "赵六", 1, DateUtils.parseDate("2025-05-07"), "zhdsfgxxn@163.com", 604, BigDecimal.valueOf(435.010));
        UserWriteModel model5 = new UserWriteModel("第三团队", "孙七", 0, DateUtils.parseDate("2025-05-08"), "zhdsfgxxn@163.com", 604, BigDecimal.valueOf(405.231));



        List<UserWriteModel> list = Arrays.asList(model1, model2, model3, model4, model5);
        EasyExcel.write(FILE_NAME).sheet("测试").head(UserWriteModel.class)
               // .registerWriteHandler(new OnceAbsoluteMergeStrategy(2,3,0,0))
//                .registerWriteHandler(new LoopMergeStrategy(2,0))
                .registerWriteHandler(new CustomMergeStrategy())
                .doWrite(list);
    }

在这里插入图片描述

但是一般是合并指定列相同的数据

  1. 自定义合并单元格配置类
package cn.js.converter;

import com.alibaba.excel.metadata.Head;
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 java.util.List;


/**
 * Description:
 *
 * @Author Js
 * @Create 2025-05-04 14:54
 * @Version 1.0
 */
public class CustomMergeStrategy implements CellWriteHandler {


    //合并列名集合
    private final List<String> mergeColumnNames;

    public CustomMergeStrategy(List<String> mergeColumnNames) {
        this.mergeColumnNames = mergeColumnNames;
    }


    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

        // 如果是表头,不做处理
        if (isHead) {
            return;
        }
        //relativeRowIndex: 当前行号,如果当前是第一行,则不做处理
        if (relativeRowIndex == 0) {
            return;
        }

        //如果不是合并列,不做处理
        if (!mergeColumnNames.contains(head.getFieldName())) {
            return;
        }


        //获取当前表格,当前行下标,上一行下标,上一行对象,上一列对象
        Sheet sheet = cell.getSheet();
        int rowIndexCurrent = cell.getRowIndex();
        int lastRowIndex = rowIndexCurrent - 1;
        Row rowPrev = sheet.getRow(lastRowIndex);
        Cell cellPrev = rowPrev.getCell(cell.getColumnIndex());

        //获取当前单元格的值
        Object cellValueCurrent = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
        //获取上一单元格的值
        Object cellValuePrev = cellPrev.getCellType() == CellType.STRING ? cellPrev.getStringCellValue() : cellPrev.getNumericCellValue();

        //如果当前单元格的值和上一单元格的值相同,则合并单元格
        if (cellValueCurrent.equals(cellValuePrev)) {
            Boolean isMerged = false;
            //获取已有的合并区域
            for (int i = 0; i < sheet.getNumMergedRegions(); i++) {
                org.apache.poi.ss.util.CellRangeAddress range = sheet.getMergedRegion(i);
                if (range.isInRange(rowIndexCurrent, cell.getColumnIndex())) {
                    //如果当前单元格已经在合并区域中,则先移除合并区域
                    sheet.removeMergedRegion(i);
                    //重新合并单元格的结束行
                    range.setLastRow(rowIndexCurrent);
                    //重新添加合并区域
                    sheet.addMergedRegion(range);
                    isMerged = true;
                    break;
                }
            }
            if (!isMerged) {
                //如果当前单元格不在合并区域中,则直接添加新的合并区域
                sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(lastRowIndex, rowIndexCurrent, cellPrev.getColumnIndex(), cellPrev.getColumnIndex()));
            }

        }
    }
}

  1. 测试类
@Test
    public void objectList3() throws ParseException {
        UserWriteModel model1 = new UserWriteModel("第一团队", "张三", 1, DateUtils.parseDate("2025-05-04"), "zhangsan@163.com", 123, BigDecimal.valueOf(123.456));
        UserWriteModel model2 = new UserWriteModel("第一团队", "张三", 0, DateUtils.parseDate("2025-05-05"), "lisi@163.com", 789, BigDecimal.valueOf(789.258));
        UserWriteModel model3 = new UserWriteModel("第二团队", "王五", 1, DateUtils.parseDate("2025-05-06"), "zhangsxxn@163.com", 124, BigDecimal.valueOf(123.056));
        UserWriteModel model4 = new UserWriteModel("第二团队", "赵六", 1, DateUtils.parseDate("2025-05-07"), "zhdsfgxxn@163.com", 604, BigDecimal.valueOf(435.010));
        UserWriteModel model5 = new UserWriteModel("第三团队", "孙七", 0, DateUtils.parseDate("2025-05-08"), "zhdsfgxxn@163.com", 604, BigDecimal.valueOf(405.231));


        List<UserWriteModel> list = Arrays.asList(model1, model2, model3, model4, model5);
        EasyExcel.write(FILE_NAME).sheet("测试").head(UserWriteModel.class)
                // .registerWriteHandler(new OnceAbsoluteMergeStrategy(2,3,0,0))
//                .registerWriteHandler(new LoopMergeStrategy(2,0))
                .registerWriteHandler(new CustomMergeStrategy(Arrays.asList("teamName", "userGender")))//设置那些列合并
                .doWrite(list);
    }

可以看到只合并我们设置的列

在这里插入图片描述

7、导出动态表头

在这里插入图片描述

语法
  1. 可以通过之前学习的 设置排除那些列导出.

    //1.显示隐藏
    @Excellgnore//在那个模型映射字段添加该注解,这个字段在导出的,就会忽略掉 
    
    //2. 设置那些模型字段导出
        EasyExcel.write(导出目标)
            .head(映射模型.class)
            //根据集合设置,那些列不导出,Collection<String>:那些模型映射类的字段,如:userName,userGender,将这些封装成Lsit集合。
            .excludeColumnFiledNames(Collection<String>)
            //根据集合设置,那些列导出
            .includeColumnFiledNames(Collection<String>)
         .doWrite(集合数据);  
    
  2. 指定表头集合

    1. 定义:根据需求动态构建表头集合。

      List<List<String>> headList = new ArrayList<>());
      headList.add(collections.singletonList("用户昵称"));
      headList.add(collections.singleonList("用户性别"));
      headList.add(collections.singletonList("用户年龄"));
      headList.add(Collections.singletonList("账户佣金"));
      headList.add(collections.singletonList("账户积分"));
      
    2. 处理:使用动态表头导出相关数据。

      EasyExcel.write(file).sheet().head(headList)
      doWrite(数据集合);
      

自定义导出动态表头
  1. 模型类
package cn.js.model;

import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;

/**
 * Description:
 *
 * @Author Js
 * @Create 2025-05-04 15:49
 * @Version 1.0
 */
@Data
@TableName("tb_user_info")
public class UserInfoEntity implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.ASSIGN_ID)
    @ExcelProperty(value = {"用户信息", "用户ID"})
    private Long id;

    @TableField(value = "user_nickname")
    @ExcelProperty(value = {"用户信息", "用户昵称"})
    private String userNickname;

    @TableField(value = "user_phone")
    @ExcelProperty(value = {"账号信息", "用户手机"})
    private String userPhone;

    @TableField(value = "user_email")
    @ExcelProperty(value = {"账号信息", "用户邮箱"})
    private String userEmail;

    @TableField(value = "user_gender")
    @ExcelProperty(value = {"其他信息", "用户性别"})
    private Integer userGender;

    @TableField(value = "user_birth")
    @ExcelProperty(value = {"其他信息", "用户生日"})
    private Date userBirth;

    @TableField(value = "user_score")
    @ExcelProperty(value = {"其他信息", "用户积分"})
    private Integer userScore;

    @TableField(value = "user_reward")
    @ExcelProperty(value = {"其他信息", "用户奖励"})
    private BigDecimal userReward;


}

  1. 测试类
@Test
    public void test4() {
        // 导出字段名称。
        List<String> exportFieldNames = Arrays.asList("id", "userNickname", "userPhone", "userEmail", "userScore");

        //解析字段名称 -> 数据表列名称。
        Map<String, String[]> map = this.resolveFieldNameMap(UserInfoEntity.class, exportFieldNames);
        //动态查询。
        List<String> columnNames = new ArrayList<>(map.keySet());
        QueryWrapper<UserInfoEntity> queryWrapper = Wrappers.<UserInfoEntity>query().select(columnNames);
        List<UserInfoEntity> list = this.userInfoService.list(queryWrapper);
        //动态表头。
        List<List<String>> headlist = map.values().stream().map(Arrays::asList).collect(Collectors.toList());
        EasyExcel.write("C:\\Users\\lenovo\\Desktop\\123\\export456.xlsx")
                .sheet("用户信息")
                .head(headlist)
                .doWrite(list);
    }

    /**
     * 解析字段名称 -> 数据表列名称。
     *
     * @param clz              实体类
     * @param exportFieldNames 导出字段名称。
     * @param <T>
     * @return 返回数据表列名,表头名称
     */
    @SneakyThrows
    private <T> Map<String, String[]> resolveFieldNameMap(Class<T> clz, List<String> exportFieldNames) {
        Map<String, String[]> map = new LinkedHashMap<>(exportFieldNames.size());
        for (String fieldName : exportFieldNames) {
            Field field = clz.getDeclaredField(fieldName);
            String columnName = field.isAnnotationPresent(TableId.class) ? field.getAnnotation(TableId.class).value() : field.getAnnotation(TableField.class).value();
            String[] headNames = field.getAnnotation(ExcelProperty.class).value();
            map.put(columnName, headNames);


        }

        return map;
    }

8、导出超链接、批注、公式

导出超链接语法
1.属性类型
@ExcelProperty(value="用户编号")
@ColumnWidth(value=15)
private WriteCellData<String>userNumber;

2. 导出处理
//声明:超链接数据。
HyperlinkData hyperlinkData = new HyperlinkData();
hyperlinkData.setHyperlinkType(HyperlinkData.HyperlinkType.URL);
hyperlinkData.setAddress("https://www.example.com/u/1001")
    
//处理:使用超链接数据初始化用户编号。
WriteCellData<String> userNumber = new WriteCellData<>("1001");
userNumber.setHyperlinkData(hyperlinkData);

//封装:模型对象。
UserInfoModel userInfoModel = new UserInfoModel();
userInfoModel.setUserNumber(userNumber);

测试

  1. 定义模型类,设置超链接字段
package cn.js.model;

import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;

/**
 * Description:
 *
 * @Author Js
 * @Create 2025-05-04 15:49
 * @Version 1.0
 */
@Data
@TableName("tb_user_info")
public class UserInfoEntity implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.ASSIGN_ID)
    @ExcelProperty(value = {"用户信息", "用户ID"})
    private Long id;

    @TableField(value = "user_nickname")
    @ExcelProperty(value = {"用户信息", "用户昵称"})
    private String userNickname;

    @TableField(value = "user_phone")
    @ExcelProperty(value = {"账号信息", "用户手机"})
    private String userPhone;

    @TableField(value = "user_email")
    @ExcelProperty(value = {"账号信息", "用户邮箱"})
    private String userEmail;

    @TableField(value = "user_gender")
    @ExcelProperty(value = {"其他信息", "用户性别"})
    private Integer userGender;

    @TableField(value = "user_birth")
    @ExcelProperty(value = {"其他信息", "用户生日"})
    private Date userBirth;

    @TableField(value = "user_score")
    @ExcelProperty(value = {"其他信息", "用户积分"})
    private Integer userScore;

    @TableField(value = "user_reward")
    @ExcelProperty(value = {"其他信息", "用户奖励"})
    private BigDecimal userReward;


}

  1. 测试
@Test
    public void test3() {
        // 查询:用户信息实体。
        List<UserInfoEntity> entityList = this.userInfoService.list();
    //转换:entity -> model
        List<UserInfoEntityModel> modelList = entityList.stream().map(item -> {
            UserInfoEntityModel model = new UserInfoEntityModel();
            //用户标识。
            model.setId(item.getId());
    // 用户昵称。
            HyperlinkData hyperlinkData = new HyperlinkData();
            hyperlinkData.setHyperlinkType(HyperlinkData.HyperlinkType.URL);
            hyperlinkData.setAddress("http://www.xxx.com/usen/" + item.getId());
            WriteCellData<String> userNickname = new WriteCellData<>(item.getUserNickname());
            userNickname.setHyperlinkData(hyperlinkData);
            model.setUserNickname(userNickname);
    //用户性别。
            model.setUserGender(item.getUserGender());
    //用户生日。
            model.setUserBirth(item.getUserBirth());
            return model;
        }).collect(Collectors.toList());
        EasyExcel.write("C:\\Users\\lenovo\\Desktop\\123\\export156.xlsx").sheet("用户信息")
                .head(UserInfoEntityModel.class)
                .doWrite(modelList);


    }

在这里插入图片描述

在这里插入图片描述


导出批注语法
//属性类型:
@ExcelProperty(value = "用户昵称")
@ColumnWidth(value = 15)
private WriteCellData<String> userName;//设置批注

//2.导出处理:
//声明:批注数据。
CommentData commentData = new CommentData();
commentData.setRichTextStringData(new RichTextStringData("此用户一天不学习就浑身难受。"));


//处理:使用批注数据初始化用户昵称。
WriteCellData<String> userName = new WriteCellData<>("Jshuai ");
userName.setCommentData(commentData);
// 封装:用户信息模型。
UserinfoModel userInfoModel = new UserlnfoModel();
userInfoModel.setUserName(userName);

测试

  1. 定义模型类,设置批注字段
@ExcelProperty(value = "用户标识")
private WriteCellData<Long> id;//设置批注
  1. 测试
 @Test
    public void test4() {
        // 查询:用户信息实体。
        List<UserInfoEntity> entityList = this.userInfoService.list();
        //转换:entity -> model
        List<UserInfoEntityModel> modelList = entityList.stream().map(item -> {
            UserInfoEntityModel model = new UserInfoEntityModel();
            
            //添加批注
            CommentData commentData = new CommentData();//创建批注对象
            commentData.setAuthor("作者:Jshuai");//设置批注的作者
            commentData.setRichTextStringData(new RichTextStringData("备注:这是一个备注信息"));
            WriteCellData<Long> id = new WriteCellData<>(BigDecimal.valueOf(item.getId()));//设置在那个字段上面添加批注
            id.setCommentData(commentData);//设置批注信息
            model.setId(id);//将设置批注信息与字段绑定

            //添加超链接
            HyperlinkData hyperlinkData = new HyperlinkData();//创建超链接对象
            hyperlinkData.setHyperlinkType(HyperlinkData.HyperlinkType.URL);//设置超链接类型
            hyperlinkData.setAddress("http://www.xxx.com/usen/" + item.getId());//设置超链接地址
            WriteCellData<String> userNickname = new WriteCellData<>(item.getUserNickname());//设置在那个字段上面添加超链接
            userNickname.setHyperlinkData(hyperlinkData);//设置超链接信息
            model.setUserNickname(userNickname);//将超链接信息与字段绑定


            //用户性别。
            model.setUserGender(item.getUserGender());
            //用户生日。
            model.setUserBirth(item.getUserBirth());
            return model;
        }).collect(Collectors.toList());
        EasyExcel.write("C:\\Users\\lenovo\\Desktop\\123\\export156.xlsx").sheet("用户信息")
                .head(UserInfoEntityModel.class)
                .doWrite(modelList);


    }

在这里插入图片描述


导出公式语法
	属性类型
    @ExcelProperty(value = "用户年龄")
    @ColumnWidth(value = 15)
    private WriteCellData<Void> userAge;

    导出处理:
    //声明:公式数据。
    FormulaData formulaData = new FormulaData();
    formulaData.setFormulaValue("CONCAT(DATEDIF(C2,NOW(),\"Y\"),\"岁\")");
    //处理:使用公式数据初始化用户年龄。
    WriteCellData<Void> userAge = new WriteCellData<>();
    userAge.setFormulaData(formulaData);
// 封装:用户信息模型。
    UserinfoModel userInfoModel=new UserinfoModel();
    userInfoModel.setUserBirth(DateUtils.parseDate("2000-01-01"));
    userInfoModel.setUserAge(userAge);

测试

  1. 定义模型类,设置公式字段
	//用户生日。
   @ExcelProperty(value = "用户生日")
   private WriteCellData<Void> userAge;
  1. 测试
@Test
   public void test6(){
// 查询:用户信息实体。
       List<UserInfoEntity> entityList = this.userInfoService.list();

       // 转换:entity -> model.
       List<UserInfoEntityModel> modelList = IntStream.range(0, entityList.size())
               .mapToObj(index -> {
                   // 提取:当前用户信息实体。
                   UserInfoEntity entity = entityList.get(index);
                   // 封装:用户信息模型。
                   UserInfoEntityModel model = new UserInfoEntityModel();
                   // 行号。
                   model.setRowNum(index + 1);
                   //添加批注
                   CommentData commentData = new CommentData();//创建批注对象
                   commentData.setAuthor("作者:Jshuai");//设置批注的作者
                   commentData.setRichTextStringData(new RichTextStringData("备注:这是一个备注信息"));
                   WriteCellData<Long> id = new WriteCellData<>(BigDecimal.valueOf(entity.getId()));//设置在那个字段上面添加批注
                   id.setCommentData(commentData);//设置批注信息
                   model.setId(id);//将设置批注信息与字段绑定
                   // 用户昵称。
                   HyperlinkData hyperlinkData = new HyperlinkData();
                   hyperlinkData.setHyperlinkType(HyperlinkData.HyperlinkType.URL);
                   hyperlinkData.setAddress("http://www.xxx.com/user/" + entity.getId());
                   WriteCellData<String> userNickname = new WriteCellData<>(entity.getUserNickname());
                   userNickname.setHyperlinkData(hyperlinkData);
                   model.setUserNickname(userNickname);
                   // 用户性别。
                   model.setUserGender(entity.getUserGender());
                   // 用户生日。
                   model.setUserBirth(entity.getUserBirth());
                   // 用户年龄。
                   FormulaData formulaData = new FormulaData();
                   formulaData.setFormulaValue("CONCAT(DATEDIF(E" + (index + 2) + ", NOW(), \"Y\"), \"岁\")");
                   WriteCellData<Void> userAge = new WriteCellData<>();
                   userAge.setFormulaData(formulaData);
                   model.setUserAge(userAge);
                   return model;
               })
               .collect(Collectors.toList());

       EasyExcel.write("C:\\Users\\lenovo\\Desktop\\123\\export156.xlsx")
               .sheet("用户信息")
               .head(UserInfoEntityModel.class)
               .doWrite(modelList);
   }

9、导出图片内容

在这里插入图片描述

导出图片内容语法
  1. 导出图片类型
@ContentRowHeight(value=50)
    @ColumnWidth(value=30)
    public class ExportModel {
        @ExcelProperty(value = "File类型")
        private File fileImage;
        
        @ExcelProperty(value = "byte[]类型")
        private byte[] byteImage;
        
        @ExcelProperty(value = "Inputstream类型")
        private InputStream inputStreamImage;
        
        @ExcelProperty(value = "URL类型")
        private URL urlImage;
        @ExcelProperty(value = "string类型", converter = StringImageConverter.class)//不设置转换器的话,导出时是一个字符串
        private String stringImage;
    }
  1. 基本语法
File file = new File("E:\\export.xlsx");
    // 封装:模型对象。
    ExportModel exportModel = new ExportModel();
                exportModel.setFileImage(图片的File对象);
                exportModel.setByteImage(图片的byte[] 对象);
                exportModel.setInputStreamImage(图片的InputStream对象);
                exportModel.setUrlImage(图片的URL对象);
                exportModel.setStringImage(图片文件路径);
//处理:导出操作。
EasyExcel.write(file)
            .sheet()
            .head(ExportModel .class)
            .doWrite(Collections.singletonList(exportModel));

3、模版填充

1、模版填充对象

在这里插入图片描述

基本语法Map填充
//封装:Map对象:
Map<String,Object> fillData = new HashMap<>();
        fillData.put("userNumber","1001");
        fillData.put("userName","Jshuai");
        fillData.put("userAccount",BigDecimal.valueOf(666.66));
        fillData.put("userscore",888);

//处理:导出操作。
EasyExcel.write(导出目标)
.withTemplate(模板文件)
.sheet()
.doFill(fillData))//数据填充

测试

@Test
    public void test7(){
        InputStream resourceAsStream = EasyExcelWiteTest.class.getClassLoader().getResourceAsStream("export-template.xlsx");

        //封装:Map对象:
        Map<String,Object> fillData = new HashMap<>();
        fillData.put("userNumber","1001");
        fillData.put("userName","Jshuai");
        fillData.put("userAccount",BigDecimal.valueOf(666.66));
        fillData.put("userscore",888);

//处理:导出操作。
        EasyExcel.write("C:\\Users\\lenovo\\Desktop\\123\\export-template.xlsx")
                .withTemplate(resourceAsStream)
                .sheet()
                .doFill(fillData);
    }

在这里插入图片描述


基本语法对象填充
  1. 实体类
@Data
public class UserModel {

       private String userNumber;
       private String userName;
       private BigDecimal userAccount;
       private Integer userScore;
}
  1. 测试
@Test
   public void test8(){
       InputStream resourceAsStream = EasyExcelWiteTest.class.getClassLoader().getResourceAsStream("export-template.xlsx");

       //封装:Map对象:
       UserModel userModel=new UserModel();
       userModel.setUserNumber("1001");
       userModel.setUserName("JshuaiBoss");
       userModel.setUserAccount(BigDecimal.valueOf(666.66));
       userModel.setUserScore(888);

//处理:导出操作。
       EasyExcel.write("C:\\Users\\lenovo\\Desktop\\123\\export-template.xlsx")
               .withTemplate(resourceAsStream)
               .sheet()
               .doFill(userModel);
   }

在这里插入图片描述

2、模版填充列表

批量填充时,模版设置时,在在占位符前面加一个点

在这里插入图片描述

语法
//封装:List对象:
List<Object> fillData = new ArrayList<>();
        

//处理:导出操作。
EasyExcel.write(导出目标)
.withTemplate(模板文件)
.sheet()
.doFill(list))//数据填充

测试

@Test
    public void test9(){
        InputStream resourceAsStream = EasyExcelWiteTest.class.getClassLoader().getResourceAsStream("export-template.xlsx");

        //封装:Map对象:
        UserModel userModel=new UserModel();
        userModel.setUserNumber("1001");
        userModel.setUserName("JshuaiBoss");
        userModel.setUserAccount(BigDecimal.valueOf(666.66));
        userModel.setUserScore(888);

        UserModel userModel2=new UserModel();
        userModel2.setUserNumber("1002");
        userModel2.setUserName("JshuaiBoss2");
        userModel2.setUserAccount(BigDecimal.valueOf(666.77));
        userModel2.setUserScore(999);
        List<UserModel> list = Arrays.asList(userModel, userModel2);

//处理:导出操作。
        EasyExcel.write("C:\\Users\\lenovo\\Desktop\\123\\export-template.xlsx")
                .withTemplate(resourceAsStream)
                .sheet()
                .doFill(list);
    }

在这里插入图片描述

3、模版填充组合

在这里插入图片描述

基本语法
try (ExcelWriter excelWriter = EasyExcel.write(..).withTemplate(..)...){
            WriteSheet writeSheet = EasyExcel.writerSheet().build();
            //处理:填充公司信息对象。
            excelWriter.fill(<公司信息对象 >,writeSheet);
            //处理:填充部门集合列表。
            FillConfig fillconfig = Fillconfig.builder()
                    .direction(WriteDirectionEnum.HORIZONTAL)//横向,默认纵向
                    .build();
            excelWriter.fill(new FillWrapper("dept", <部门集合>), fillconfig, writeSheet);
            //处理:填充员工集合列表。
            excelWriter.fill(new FillWrapper("employee", <员工集合>),writeSheet);

        }

在这里插入图片描述

测试

@Test
    public void test10() {
        InputStream exportTemplate = EasyExcelWiteTest.class.getClassLoader().getResourceAsStream("export-templates.xlsx");

        try (ExcelWriter excelWriter = EasyExcel.write("C:\\Users\\lenovo\\Desktop\\123\\export12026.xlsx")
                .withTemplate(exportTemplate).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet().build();

            // 公司信息。
            Map<String, Object> companyInfo = new HashMap<>();
            companyInfo.put("companyName", "大明帝国信息技术有限公司");
            excelWriter.fill(companyInfo, writeSheet);


            // 部门信息。
            Map<String, Object> deptInfo1 = new HashMap<>();
            deptInfo1.put("deptName", "研发部");
            deptInfo1.put("deptMaster", "张先生");
            deptInfo1.put("deptContact", "186****7420");

            Map<String, Object> deptInfo2 = new HashMap<>();
            deptInfo2.put("deptName", "设计部");
            deptInfo2.put("deptMaster", "李女士");
            deptInfo2.put("deptContact", "185****7420");

            List<Map<String, Object>> deptList = Arrays.asList(deptInfo1, deptInfo2);
            FillConfig fillconfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); // 填充配置
            excelWriter.fill(new FillWrapper("dept", deptList), fillconfig, writeSheet);// 填充部门信息


            // 员工信息。
            Map<String, Object> empInfo1 = new HashMap<>();
            empInfo1.put("userNumber", "A001002");
            empInfo1.put("userName", "张三");
            empInfo1.put("userAccount", 12);
            empInfo1.put("userscore", BigDecimal.valueOf(120));

            Map<String, Object> empInfo2 = new HashMap<>();
            empInfo2.put("userNumber", "A001005");
            empInfo2.put("userName", "李四");
            empInfo2.put("userAccount", 16);
            empInfo2.put("userscore", BigDecimal.valueOf(24));
            List<Map<String, Object>> empList = Arrays.asList(empInfo1, empInfo2);

            excelWriter.fill(new FillWrapper("user", empList), writeSheet);// 填充员工信息

        }
    }

在这里插入图片描述


4、导出文件下载

语法
 //1.处理:设置响应参数(内容类型、字符集编码)。
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        
//2.处理:设置文件名。
String fileName = URLEncoder.encode("用户列表","UTF-8").replaceAll("\\+","%20");
response.setHeader("content-Disposition", "attachment;filename*=UTF-8’" + fileName + ".xlsx");

//3.处理:导出文件到输出流。
InputStream inputStream = Application.class.getclassloader().getResourceAsStream("user-export-template.xlsx");
OutputStream outputStream=response.getOutputStream();EasyExcel.write(outputStream);
EasyExcel.write(outputStream)
    .withTemplate(inputStream)
    .sheet("用户列表")
    .doFill(list);
<think>好的,我现在需要回答用户关于使用EasyExcel的问题。首先,我得回忆一下EasyExcel的基本信息和常见用途。EasyExcel是阿里巴巴开源的一个Java库,用于简化Excel文件的读写操作。相比Apache POI,它更节省内存,适合处理大文件。用户可能需要知道如何快速开始使用它,或者解决常见问题。 首先,我应该介绍EasyExcel的核心优势,比如内存优化、模型映射和监听器机制。然后,给出具体的代码示例,比如如何读写Excel。读写示例需要包括依赖添加、创建模型类、实现读写逻辑。例如,写文件需要创建数据对象列表,使用EasyExcel.write方法;读文件则需要定义监听器,并调用read方法。 接下来,要解释为什么使用EasyExcel,可能涉及与传统POI的对比,比如POI的内存溢出问题,以及EasyExcel的逐行处理方式如何减少内存占用。此外,提到事件驱动模型和注解支持的好处,比如简化代码,提高可维护性。 然后,考虑用户可能遇到的问题,比如如何处理复杂格式,或者如何自定义转换器。但根据用户的问题,可能更关注基础使用,所以先提供基础示例和解释。 最后,按照要求,提出相关问题,这些问题应该围绕EasyExcel的特性、使用场景、与传统工具的区别,以及常见问题处理。例如,如何处理大数据量、注解的作用、监听器的用途,以及样式自定义方法。 现在,检查是否有遗漏点,比如Maven依赖的配置是否正确,是否需要提醒用户注意版本兼容性。另外,确保代码示例正确,比如模型类使用了正确的注解,读写方法参数正确。例如,写示例中的文件路径是否正确,读示例中的监听器是否实现了适当的方法。 总结,回答应包括简介、优势、代码示例、解释,然后相关问题。需要确保信息准确,结构清晰,符合用户要求的格式。</think><< 使用EasyExcel进行Excel读写操作的实现方法及原因分析: 1. 核心优势: - 内存优化:采用逐行解析模式,在处理大型Excel文件时内存占用可控制在百KB级别 - 简单API:通过注解驱动方式简化开发,支持复杂对象映射 - 扩展性强:提供监听器机制支持自定义处理逻辑 2. 写Excel示例: // 添加Maven依赖 // <dependency> // <groupId>com.alibaba</groupId> // <artifactId>easyexcel</artifactId> // <version>3.3.2</version> // </dependency> // 定义数据模型 @Data public class UserData { @Ex
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北执南念

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值