Java使用EasyExcel进行excel读写操作指南

 更新时间:2026年06月11日 09:16:29   作者:西凉的悲伤  
EasyExcel是一个专为Java设计的高效Excel处理工具,它旨在帮助开发者在处理大文件时避免内存溢出问题,同时提供简单易用的API来进行Excel的读写操作,本文给大家介绍了Java使用EasyExcel进行excel读写操作指南,需要的朋友可以参考下

前言

EasyExcel是一个专为Java设计的高效Excel处理工具,它旨在帮助开发者在处理大文件时避免内存溢出问题,同时提供简单易用的API来进行Excel的读写操作。相较于Apache POI和jxl等其他库,EasyExcel通过优化的解析算法显著降低了内存消耗,特别是对于07版以上的Excel文件,其内存占用量远低于使用POI SAX模式解析时的需求,几乎消除了内存溢出的风险。此外,EasyExcel对03版Excel的支持则是基于POI的SAX模式并进行了进一步的模型转换封装,提高了使用的便捷性。

一、依赖

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

二、读取excel指定sheet页数据分批处理

假设有 excel 如下

1.excel列名对应的映射实体类

使用@ExcelProperty(value = “”)来标识excel列名

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

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

@Data
public class RowData {
    
    /** 企业订单号 */
    @ExcelProperty(value = "企业订单号")
    private String enterpriseOrderNo;
  
    
    /** 所在部门(新)编号 */
    @ExcelProperty(value = "所在部门(新)编号")
    private Integer departmentNewNo;
    
    /** 乘车人姓名 */
    @ExcelProperty(value = "乘车人姓名")
    private String passengerName;
   
    
    /** 订单总金额 */
    @ExcelProperty(value = "订单总金额")
    private BigDecimal totalOrderAmount;
    
    /** 企业实付金额 */
    @ExcelProperty(value = "企业实付金额")
    private BigDecimal enterpriseActualPayment;
   
    
    /** 订单类型 */
    @ExcelProperty(value = "订单类型")
    private String orderType;
    
    /** 下单时间 */
    @ExcelProperty(value = "下单时间")
    private Date orderTime;
}

2.监听类,用于处理读取的excel行数据

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson2.JSON;
import hai.tang.model.RowData;

import java.util.List;

// 有个很重要的点 RowDataListener 不能被spring管理,所以如果使用spring要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class RowDataListener implements ReadListener<RowData> {

    /**
     * 每1000条存储数据库或者进行其他操作,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 1000;
    /**
     * 缓存的数据
     */
    private List<RowData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    //   private DemoDAO demoDAO;

//    public RowDataListener() {
    // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
//        demoDAO = new DemoDAO();
//    }

    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param demoDAO
     */
//    public RowDataListener(DemoDAO demoDAO) {
//        this.demoDAO = demoDAO;
//    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(RowData data, AnalysisContext context) {
        System.out.println("解析到一条数据:{}" + JSON.toJSONString(data));
        cachedDataList.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

   /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        System.out.println("所有数据解析完成!");
    }

    /**
     * 存储到数据库,或者可改为对数据进行其他操作
     */
    private void saveData() {
        System.out.println("{}条数据,开始存储数据库!" + cachedDataList.size());
        //demoDAO.save(cachedDataList);
        System.out.println("存储数据库成功!");
    }
}

3.测试

        String fileName = "C:\\Users\\LAPtOP\\Desktop\\工作簿1.xlsx";
        // 这里 需要指定读用哪个class去读,然后读取"用车订单"sheet,结束后文件流会自动关闭
        EasyExcel.read(fileName, RowData.class, new RowDataListener()).sheet("用车订单").doRead();

三、数据写入excel

1.数据量比较小可以一次性写入

        String fileName = "C:\\Users\\LAPtOP\\Desktop\\工作簿1.xlsx";
		//从数据库查询出的数据,或者构造的数据
        List<RowData> list = ...;
		//如果数据只有一万行左右,可以一次性写入。将数据写入 "用车订单" sheet 页
        EasyExcel.write(fileName, RowData.class).sheet("用车订单").doWrite(list);

2.数据量大,分批多次写入

        String fileName = "C:\\Users\\LAPtOP\\Desktop\\工作簿1.xlsx";
        try (ExcelWriter excelWriter = EasyExcel.write(fileName, RowData.class).build()) {
            // 这里注意 如果同一个sheet只要创建一次
            WriteSheet writeSheet = EasyExcel.writerSheet("用车订单").build();
            // 这里调用了五次,每次获得一批数据写入。实际使用时根据数据库分页的总的页数来
            for (int i = 0; i < 5; i++) {
                // 分页去数据库查询数据或者构建数据 这里可以去数据库查询每一页的数据
                List<RowData> list = ...;
                excelWriter.write(list, writeSheet);
            }
        }

四、excel文件的上传和下载

1.下载

/**
    * 文件下载(失败了会返回一个有部分数据的Excel)
    * <p>
    * 1. 创建excel对应的实体对象 参照{@link DownloadData}
    * <p>
    * 2. 设置返回的 参数
    * <p>
    * 3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大
    */
    @GetMapping("download")
    public void download(HttpServletResponse response) throws IOException {
        // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName=URLEncoder.encode("测试","UTF-8").replaceAll("\\+","%20");
        response.setHeader("Content-disposition","attachment;filename*=utf-8''"+fileName+".xlsx");
		List<RowData> list = ...;
        EasyExcel.write(response.getOutputStream(),RowData.class).sheet("sheet").doWrite(list);
    }

2. 上传

    /**
     * 文件上传
     * <p>1. 创建excel对应的实体对象 参照{@link UploadData}
     * <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener}
     * <p>3. 直接读即可
     */
    @PostMapping("upload")
    @ResponseBody
    public String upload(MultipartFile file)throws IOException{
        EasyExcel.read(file.getInputStream(),RowData.class,new UploadDataListener(uploadDAO)).sheet().doRead();
        return"success";
    }

五、分批读取并写入综合示例

下面的代码会读取excel文件里的指定sheet页,然后读取指定列的数据,最后新建一个excel文件,把读取到的列数据写入到新建的excel文件的指定sheet里。

import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;

import java.util.List;

// 有个很重要的点 RowDataListener 不能被spring管理,所以如果使用spring要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class RowDataListener implements ReadListener<DiDiReconcileExcelParseModel> {

    /**
     * 每读取1000条数据进行操作,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 1000;
    /**
     * 缓存的数据
     */
    private List<DiDiReconcileExcelParseModel> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    /**
     * 用于写入excel
     */
    private ExcelWriter excelWriter = null;
    /**
     * 用于写入excel
     */
    private WriteSheet writeSheet = null;


    public RowDataListener(ExcelWriter excelWriter, WriteSheet writeSheet) {
        this.excelWriter = excelWriter;
        this.writeSheet = writeSheet;
    }

    /**
     * 获取表头和其他行的水平和垂直居中设置
     */
    public static HorizontalCellStyleStrategy getHorizontalCellStyleStrategy() {
        // 设置表头单元格样式
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 设置水平居中
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 设置垂直居中
        headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

        // 设置除表头外的其他行的内容单元格样式
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        // 设置水平居中
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 设置垂直居中
        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

        // 使用自定义样式策略
       return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }


    /**
     * 每一条数据解析都会调用该方法一次
     *
     * @param data    one row value. Is is same as
     * @param context
     */
    @Override
    public void invoke(DiDiReconcileExcelParseModel data, AnalysisContext context) {
        //System.out.println("解析到一条数据:" + JSON.toJSONString(data));
        cachedDataList.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    /**
     * 当所有数据全部解析读取完后 会调用该方法一次
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        System.out.println("所有数据写入完成!");
        excelWriter.finish();
        if (excelWriter != null) {
            excelWriter.close();
        }
    }

    /**
     * 保存数据
     */
    private void saveData() {
        System.out.println("写入" + cachedDataList.size()+"条数据");
        excelWriter.write(cachedDataList, writeSheet);
    }
}

测试:

        String writeFile = "C:\\Users\\LEPLOP\\Desktop\\写入.xlsx";
        ExcelWriter excelWriter = EasyExcel.write(writeFile, DiDiReconcileExcelParseModel.class).build();
        WriteSheet writeSheet = EasyExcel.writerSheet("用车订单")
                //设置垂直和水平居中
                .registerWriteHandler(RowDataListener.getHorizontalCellStyleStrategy())
                .build();

        //从 readFile 中的 "用车订单" sheet中读取指定的列数据(DiDiReconcileExcelParseModel 里指定的列),然后通过excelWriter, writeSheet 写入到 writeFile中
        String readFile = "C:\\Users\\LEPLOP\\Desktop\\test\\账单.xlsx";
        EasyExcel.read(readFile, DiDiReconcileExcelParseModel.class, new RowDataListener(excelWriter, writeSheet))
                .sheet("用车订单")
                .doRead();

根据测试,读取一个14万行并有216列的excel文件,然后挑选其中6列写入新的excel文件,大概耗时不到3分钟

以上就是Java使用EasyExcel进行excel读写操作指南的详细内容,更多关于Java EasyExcel进行Excel读写的资料请关注脚本之家其它相关文章!

相关文章

  • SpringBoot中@RequestBody的伪表单提交场景

    SpringBoot中@RequestBody的伪表单提交场景

    @RequestBody是Spring MVC/Spring Boot中用于处理HTTP请求体数据的核心注解,主要用于将JSON/XML格式的请求体数据自动绑定到方法参数对象上, 下面就来介绍一下如何使用
    2025-12-12
  • 在SpringBoot中使用JWT的实现方法

    在SpringBoot中使用JWT的实现方法

    这篇文章主要介绍了在SpringBoot中使用JWT的实现方法,详细的介绍了什么是JWT和JWT实战,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-12-12
  • 如何在 Spring Boot 中配置和使用 CSRF 保护

    如何在 Spring Boot 中配置和使用 CSRF 保护

    CSRF是一种网络攻击,它利用已认证用户的身份来执行未经用户同意的操作,Spring Boot 提供了内置的 CSRF 保护机制,可以帮助您防止这种类型的攻击,这篇文章主要介绍了Spring Boot 中的 CSRF 保护配置的使用方法,需要的朋友可以参考下
    2023-09-09
  • 一文搞懂Spring中的JavaConfig

    一文搞懂Spring中的JavaConfig

    这篇文章主要介绍了Spring中的JavaConfig知识,包括事务注解驱动,properties配置文件加载方法,本文给大家介绍的非常详细,需要的朋友可以参考下
    2021-09-09
  • java中JDeps命令使用

    java中JDeps命令使用

    jdeps是一个Java类依赖分析工具,用于分析Java应用程序的依赖情况,包括类、包、模块以及JDK内部API的使用,本文就来详细的介绍一下,感兴趣的可以了解一下
    2024-09-09
  • Java中时间戳和时间的转换方法代码

    Java中时间戳和时间的转换方法代码

    这篇文章主要介绍了Java中时间戳和时间的转换的相关资料,Java8中时间戳与日期时间对象之间的转换是编程中常见的操作,通过时间字符串获取时间对象也是其中的一种方法,需要的朋友可以参考下
    2025-03-03
  • SpringBoot+Druid开启监控页面的实现示例

    SpringBoot+Druid开启监控页面的实现示例

    本文主要介绍了SpringBoot+Druid开启监控页面的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-06-06
  • SpringBoot同时接收文件以及json参数实现方式

    SpringBoot同时接收文件以及json参数实现方式

    文章介绍了在前端传递参数给后端时,如果需要同时接收文件和JSON参数,应该使用`@RequestPart`注解,后端和前端需要约定好文件和JSON参数的对应名称,通常是`files`和`saveDto`,前端在发送请求时需要注意设置正确的请求头信息
    2026-01-01
  • Spring JPA事务管理与自定义操作实例解析(最新推荐)

    Spring JPA事务管理与自定义操作实例解析(最新推荐)

    在Spring框架中,数据持久化操作常常与事务管理紧密相关,本文将深入探讨Spring Data JPA中的事务管理机制,并结合具体实例,展示如何自定义事务行为以满足不同的业务需求,感兴趣的朋友一起看看吧
    2024-12-12
  • Mybatis-plus更新Null字段的四种方法

    Mybatis-plus更新Null字段的四种方法

    在项目开发过程中,经常会使用Mybatis-plus的updateById()方法,快速将接收道德参数或者查询结果中原本不为null的字段更新为null,这个时候使用updateById()并不能实现这个操作,不会报错,但是对应的字段并没有更新为null,所以本文介绍了Mybatis-plus更新Null字段的方法
    2025-03-03

最新评论