当前位置: 首页 > news >正文

基于EasyExcel实现Excel导出功能

目录

介绍

快速使用

分页查询并导出

官网参照

分页导出


介绍

        EasyExcel 是阿里巴巴开源的一个基于 Java 的简单的、节省内存的 Excel 处理工具, 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模式,在上层做了模型转换的封装,让使用者更加简单方便。

        本篇主要讲的是 EasyExcel 的导出功能,即写入功能,且分为单次写入和多次写入,想要了解更多可参考官方网站:EasyExcel官方网站

快速使用

  • 依赖引入

<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.1</version>
</dependency>
  • 示例代码

    /*** * 结合官方提供资料完成快速开始* 文件下载(失败了会返回一个有部分数据的Excel)* <p>* 1. 创建excel对应的实体对象* <p>* 2. 设置返回的 参数* <p>* 3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大*/@GetMapping("/ff/export")public void exportExcel(HttpServletResponse response) throws IOException {// 设置响应格式(Excel)response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("UTF-8");// 处理文件名中文乱码String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");// 写入数据EasyExcel.write(response.getOutputStream(), User.class).sheet("测试用户列表") .doWrite(getExportData()); }/*** 生成导出的模拟数据* @return*/public List<User> getExportData() {List<User> userList = new ArrayList<>();for (int i = 1; i <= 100; i++) {User user = new User();user.setName("用户" + i);user.setAge(20 + new Random().nextInt(30)); // 年龄20-50岁user.setEmail("user" + i + "@XX.com");userList.add(user);}return userList;}/*** * Excel映射实体*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {private static final long serialVersionUID = 1L;@ExcelProperty(value = "姓名", index = 0)private String name;@ExcelProperty(value = "年龄", index = 1)private Integer age;@ExcelProperty(value = "邮箱", index = 2)private String email;
}

分页查询并导出

官网参照

重复多次写入(写到单个或者多个 Sheet)中

/*** 重复多次写入* <p>* 1. 创建excel对应的实体对象 参照{@link ComplexHeadData}* <p>* 2. 使用{@link ExcelProperty}注解指定复杂的头* <p>* 3. 直接调用二次写入即可*/@Testpublic void repeatedWrite() {// 方法1: 如果写到同一个sheetString fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写try (ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build()) {// 这里注意 如果同一个sheet只要创建一次WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来for (int i = 0; i < 5; i++) {// 分页去数据库查询数据 这里可以去数据库查询每一页的数据List<DemoData> data = data();excelWriter.write(data, writeSheet);}}// 方法2: 如果写到不同的sheet 同一个对象fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";// 这里 指定文件try (ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build()) {// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面for (int i = 0; i < 5; i++) {// 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).build();// 分页去数据库查询数据 这里可以去数据库查询每一页的数据List<DemoData> data = data();excelWriter.write(data, writeSheet);}}// 方法3 如果写到不同的sheet 不同的对象fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";// 这里 指定文件try (ExcelWriter excelWriter = EasyExcel.write(fileName).build()) {// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面for (int i = 0; i < 5; i++) {// 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样。这里注意DemoData.class 可以每次都变,我这里为了方便 所以用的同一个class// 实际上可以一直变WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).head(DemoData.class).build();// 分页去数据库查询数据 这里可以去数据库查询每一页的数据List<DemoData> data = data();excelWriter.write(data, writeSheet);}}}

分页导出

  • 模拟数据导出

  • 示例代码(主要是实现写到不同的sheet 同一个对象这种方式)

package com.ffliuli.business;import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.ffliuli.entity.User;
import org.springframework.util.CollectionUtils;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;/**** 分页读取数据可避免OOM的发生*/
@Service
public class BigDataExportBusiness {private static final int MAX_ROWS_SHEET = 1000000;private static final int PAGE_SIZE = 3000;private static final long TOTAL_DATA_COUNT = 3000000; /*** 导出*/public void exportBigData(HttpServletResponse response) throws IOException {setExcelResponseHeader(response, "300万用户列表测试");try (OutputStream outputStream = response.getOutputStream()) {// 初始化ExcelWriter,绑定输出流和数据模型ExcelWriter excelWriter = EasyExcel.write(outputStream, User.class).build();// 初始化Sheet参数,Sheet序号int currentSheetNo = 0;// 当前Sheet已写入行数int currentSheetRowCount = 0;WriteSheet currentWriteSheet = createWriteSheet(currentSheetNo, "用户列表-" + (currentSheetNo + 1));int pageNum = 1; while (true) {List<User> pageDataList = getPageUserData(pageNum, PAGE_SIZE);if (CollectionUtils.isEmpty(pageDataList)) {break; }// 检查当前Sheet是否已满,满则创建新Sheetif (currentSheetRowCount + pageDataList.size() > MAX_ROWS_SHEET) {currentSheetNo++;currentWriteSheet = createWriteSheet(currentSheetNo, "用户列表-" + (currentSheetNo + 1));currentSheetRowCount = 0;}excelWriter.write(pageDataList, currentWriteSheet);currentSheetRowCount += pageDataList.size();// 进入下一页查询pageNum++;}// 完成写入并释放资源excelWriter.finish();}}/**** 设置Excel下载响应头*/private void setExcelResponseHeader(HttpServletResponse response, String fileName) throws IOException {response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding(StandardCharsets.UTF_8.name());String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()).replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + encodedFileName + ".xlsx");response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");response.setHeader("Pragma", "no-cache");}/**** 创建新的WriteSheet*/private WriteSheet createWriteSheet(int sheetNo, String sheetName) {return EasyExcel.writerSheet(sheetNo, sheetName).build();}/**** 模拟数据库数据*/private List<User> getPageUserData(int pageNum, int pageSize) {List<User> userList = new ArrayList<>(pageSize);Random random = new Random();// 计算当前页数据的起止索引long startIndex = (pageNum - 1L) * pageSize + 1;long endIndex = Math.min(startIndex + pageSize - 1, TOTAL_DATA_COUNT);if (startIndex > TOTAL_DATA_COUNT) {return userList;}// 生成当前页模拟数据for (long i = startIndex; i <= endIndex; i++) {User user = new User();user.setName("用户" + i);user.setAge(20 + random.nextInt(30)); user.setEmail("user" + i + "@XX.com");userList.add(user);}return userList;}
}
// 以上代码仅仅是示例代码可对其进行扩展,如加上前端请求时,传输过来的条件进行分页查询后再写入excel进而导出。
// 要使其更加贴合实战项目需进行升级改造,可从不同场景进行切入。

内存使用情况

结尾

       总之,基于 EasyExcel 实现 Excel 导出功能的技术分享就完整收尾了。本文从 EasyExcel 的核心价值切入 —— 相较于 Apache POI、jxl 等传统框架,它通过重写 07 版 Excel 解析逻辑,将 3M 文件的内存占用从 100M 级降至几 M,彻底解决了大数据导出的 OOM 痛点;随后逐步落地实战:从引入 3.1.1 版本依赖、定义带@ExcelProperty注解的 User 映射实体,到编写单次导出接口(含响应格式配置、中文文件名编码处理),再到深入分页导出的三种核心场景(同 Sheet 多次写入、不同 Sheet 同对象写入、不同 Sheet 不同对象写入),最后通过 300 万用户数据导出的案例,演示了如何用 “分页查询(每页 3000 条)+ 单 Sheet 百万行上限拆分” 实现高效稳定的大数据导出,且从监控数据可见,节省堆内存,CPU 消耗极低。
        在实际项目中落地时,建议根据数据量级灵活选择方案:千级以内小数据量可直接用单次写入简化代码;百万级大数据务必优先采用分页查询 + 多 Sheet 拆分的模式,同时保留setExcelResponseHeader中的缓存控制配置,避免浏览器下载异常。如果遇到复杂表头、单元格合并、数据格式自定义等场景,或者对文中分页逻辑有更优优化思路,欢迎在评论区留言交流 。
        读者后续可以继续拓展 EasyExcel 的相关内容,比如 Excel 导入功能、异常重试机制、复杂模板填充等实战场景。若本文对你的开发有帮助,不妨点赞收藏。感谢你的耐心阅读,技术之路漫长,同行者常伴,期待与你下次交流~~~

http://www.dtcms.com/a/564500.html

相关文章:

  • 【SpringBoot】31 核心功能 - 单元测试 - JUnit5 单元测试中的断言机制——验证你的代码是否按预期执行了
  • kafka问题解决
  • Parasoft C/C++test如何在CCS3环境下进行F2812项目的单元测试
  • CCID工具,Jenkins、GitLab CICD、Arbess一文全方位对比分析
  • 公司网页设计的设计过程南昌网站排名优化报价
  • 如何查询网站空间寻甸马铃薯建设网站
  • Node.js 中的中间件机制与 Express 应用
  • 【保姆级教程】在AutoDL容器中部署EGO-Planner,实现无人机动态避障规划
  • 仿生机器鹰无人机技术解析
  • 2025无人机在电力交通中的应用实践
  • Qt实时绘制飞行轨迹/移动轨迹实时显示/带旋转角度/平滑移动/效果一级棒/地面站软件开发/无人机管理平台
  • 八股已死、场景当立(场景篇-负载均衡篇)
  • Go语言设计模式:备忘录模式详解
  • 基于YOLOv10的无人机智能巡检系统:电力线路悬挂物检测实战
  • 定制开发开源AI智能名片S2B2C商城小程序中的羊群效应应用研究
  • seo搜索引擎优化网站店铺位置怎么免费注册定位
  • 一个专门做恐怖片的网站做化工行业网站
  • 物联网 “神经” 之以太网:温湿度传感器的工业级 “高速干道”​
  • Biotin-PEG-OH,生物素-聚乙二醇-羟基,应用领域
  • 物联网“神经”之LoRa:温湿度传感器的广域“节能使者”
  • 舆情处置的自动化实践:基于Infoseek舆情系统的技术解析与落地指南
  • jcms内容管理系统百度seo怎么查排名
  • 亚马逊旺季广告攻略:解码产品周期,精准引爆销量
  • 【C#】HTTP中URL编码方式解析
  • 高速打印,安全稳定全兼顾 至像国产芯系列M3500DNWA应用测评
  • MacOS 安装Python 3.13【同时保留旧版本】
  • 八股训练营第 6 天 | HTTPS 和HTTP 有哪些区别?HTTPS的工作原理(HTTPS建立连接的过程)?TCP和UDP的区别?
  • 多阶段构建:打造最小化的 Spring Boot Docker 镜像
  • 在windows或者mac配置安装miniforge3
  • Arbess零基础学习 - 使用Arbess+GitPuk实现Java项目构建并Docker部署