news 2026/4/20 2:25:38

别再手动复制粘贴了!用EasyExcel 2.1.1 + SpringBoot 5分钟搞定数据库数据导出Excel

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再手动复制粘贴了!用EasyExcel 2.1.1 + SpringBoot 5分钟搞定数据库数据导出Excel

5分钟极速导出Excel:SpringBoot+EasyExcel实战指南

每次产品经理甩来一句"把用户数据导出成Excel"时,你是不是还在手忙脚乱地复制粘贴?作为Java开发者,我们完全可以用技术手段优雅解决这个问题。今天要介绍的EasyExcel,能让数据库到Excel的转换变得像喝咖啡一样简单——从配置到运行,真正只需5分钟。

1. 环境准备与依赖配置

1.1 初始化SpringBoot项目

首先确保你已经有一个基础的SpringBoot项目。如果还没有,可以通过Spring Initializr快速生成:

curl https://start.spring.io/starter.zip -d dependencies=web,mysql,mybatis -d type=maven-project -o excel-export-demo.zip

解压后导入到你喜欢的IDE中。我习惯用IntelliJ IDEA,它的自动补全功能对后续开发很有帮助。

1.2 添加EasyExcel依赖

在pom.xml中加入以下依赖(注意版本号):

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

提示:最新稳定版已经迭代到3.x系列,相比2.x版本在内存占用和性能上有显著提升

2. 数据准备与模型定义

2.1 数据库表设计示例

假设我们要导出用户数据,对应的MySQL表结构如下:

CREATE TABLE `user` ( `id` bigint NOT NULL AUTO_INCREMENT, `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, `email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

2.2 定义Excel映射模型

创建一个UserExcelVO类来定义Excel的列结构:

@Data public class UserExcelVO { @ExcelProperty("用户ID") private Long id; @ExcelProperty("用户名") private String username; @ExcelProperty(value = "邮箱", converter = CustomStringConverter.class) private String email; @ExcelProperty(value = "注册时间", format = "yyyy-MM-dd HH:mm:ss") private Date createTime; }

这里用到了几个关键注解:

  • @ExcelProperty:定义列名和顺序
  • format:日期格式化
  • converter:自定义数据转换器(后面会介绍)

3. 核心导出逻辑实现

3.1 基础导出控制器

创建一个REST控制器处理导出请求:

@RestController @RequestMapping("/export") public class ExcelExportController { @Autowired private UserService userService; @GetMapping("/users") public void exportUsers(HttpServletResponse response) throws IOException { // 设置响应头 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"); // 查询数据并导出 List<UserExcelVO> data = userService.getAllUsersForExport(); EasyExcel.write(response.getOutputStream(), UserExcelVO.class) .sheet("用户数据") .doWrite(data); } }

3.2 服务层数据转换

在UserService中实现数据转换逻辑:

@Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public List<UserExcelVO> getAllUsersForExport() { List<User> users = userMapper.selectAll(); return users.stream().map(user -> { UserExcelVO vo = new UserExcelVO(); BeanUtils.copyProperties(user, vo); return vo; }).collect(Collectors.toList()); } }

4. 高级功能扩展

4.1 自定义数据转换器

当需要特殊处理某些字段时(比如加密邮箱),可以创建自定义转换器:

public class CustomStringConverter implements Converter<String> { @Override public Class<?> supportJavaTypeKey() { return String.class; } @Override public CellDataTypeEnum supportExcelTypeKey() { return CellDataTypeEnum.STRING; } @Override public String convertToJavaData(ReadConverterContext<?> context) { return context.getReadCellData().getStringValue(); } @Override public WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) { // 对邮箱进行部分脱敏处理 String email = context.getValue(); if(email != null && email.contains("@")) { String[] parts = email.split("@"); if(parts[0].length() > 3) { email = parts[0].substring(0, 3) + "***@" + parts[1]; } } return new WriteCellData<>(email); } }

4.2 大数据量分片导出

当数据量超过万条时,建议使用分片查询导出:

public void exportLargeData(HttpServletResponse response) throws IOException { // ...响应头设置同上... ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), UserExcelVO.class).build(); int pageSize = 5000; int page = 1; while(true) { List<UserExcelVO> data = userService.getUsersByPage(page, pageSize); if(data.isEmpty()) break; WriteSheet writeSheet = EasyExcel.writerSheet(page-1, "第"+page+"页").build(); excelWriter.write(data, writeSheet); page++; } excelWriter.finish(); }

4.3 动态列导出

有时需要根据用户选择动态决定导出哪些列:

public void dynamicExport(HttpServletResponse response, Set<String> includeFields) throws IOException { // ...响应头设置... ExcelWriterBuilder writerBuilder = EasyExcel.write(response.getOutputStream()); // 动态设置包含的字段 if(includeFields.contains("username")) { writerBuilder.includeColumnFieldNames(Collections.singletonList("username")); } // 其他字段同理... writerBuilder.head(UserExcelVO.class) .sheet("动态数据") .doWrite(userService.getAllUsersForExport()); }

5. 常见问题解决方案

5.1 内存溢出问题

即使使用EasyExcel,处理超大文件时仍需注意:

  • 对于导出:使用分页查询+分片写入(如4.2所示)
  • 对于导入:使用ReadListener逐行处理
// 安全导入示例 public void safeImport(MultipartFile file) { EasyExcel.read(file.getInputStream(), UserExcelVO.class, new UserDataListener(userService)) .sheet() .headRowNumber(1) // 跳过标题行 .doRead(); }

5.2 样式定制技巧

通过拦截器自定义单元格样式:

EasyExcel.write(outputStream, UserExcelVO.class) .registerWriteHandler(new CellStyleStrategy()) .sheet("带样式的数据") .doWrite(data); // 自定义样式策略 public class CellStyleStrategy implements CellWriteHandler { @Override public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { if(isHead) { // 设置标题样式 CellStyle style = writeSheetHolder.getSheet().getWorkbook().createCellStyle(); Font font = writeSheetHolder.getSheet().getWorkbook().createFont(); font.setBold(true); font.setColor(IndexedColors.WHITE.getIndex()); style.setFont(font); style.setFillForegroundColor(IndexedColors.BLUE.getIndex()); style.setFillPattern(FillPatternType.SOLID_FOREGROUND); cell.setCellStyle(style); } } }

5.3 性能优化参数

通过调整这些参数可以显著提升导出速度:

参数推荐值说明
autoCloseStreamtrue自动关闭输出流
useDefaultStylefalse禁用默认样式减少内存
compressionLevel5ZIP压缩级别(1-9)
writeCacheSize8192写缓存大小(字节)

配置示例:

EasyExcel.write(response.getOutputStream(), UserExcelVO.class) .excelType(ExcelTypeEnum.XLSX) .autoCloseStream(true) .compressionLevel(5) .sheet("优化后的导出") .doWrite(data);

6. 完整项目结构参考

一个标准的导出功能项目结构如下:

src/main/java ├── com.example.exceldemo │ ├── config │ ├── controller │ │ └── ExcelExportController.java │ ├── converter │ │ └── CustomStringConverter.java │ ├── entity │ │ ├── User.java │ │ └── UserExcelVO.java │ ├── listener │ │ └── UserDataListener.java │ ├── mapper │ │ └── UserMapper.java │ ├── service │ │ ├── impl │ │ │ └── UserServiceImpl.java │ │ └── UserService.java │ └── ExcelDemoApplication.java

在实际项目中,我发现最容易出问题的环节是响应头的设置——特别是当需要支持多浏览器下载时。经过多次测试,最可靠的响应头组合是:

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");
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/20 2:23:27

AI Agent Harness Engineering 产品设计指南:如何平衡用户体验与技术可行性?

AI Agent Harness Engineering 产品设计指南:如何平衡用户体验与技术可行性? 摘要 随着人工智能代理(AI Agent)技术的快速发展,如何有效地设计、构建和部署这些代理系统已成为业界关注的焦点。AI Agent Harness Engineering(AI代理驾驭工程)作为一个新兴领域,旨在解决这…

作者头像 李华
网站建设 2026/4/20 2:23:09

来自学习的第二天

今天是我学习编程的第二天&#xff0c;希望能够学好&#xff0c;能够学得多&#xff0c;以后当个大佬&#xff0c;我相信我一定可以的

作者头像 李华
网站建设 2026/4/20 2:21:51

TQVaultAE:如何彻底解决泰坦之旅玩家的装备管理噩梦?

TQVaultAE&#xff1a;如何彻底解决泰坦之旅玩家的装备管理噩梦&#xff1f; 【免费下载链接】TQVaultAE Extra bank space for Titan Quest Anniversary Edition 项目地址: https://gitcode.com/gh_mirrors/tq/TQVaultAE 你是否曾在《泰坦之旅周年版》中为仓库空间不足…

作者头像 李华
网站建设 2026/4/20 2:10:30

系统架构演进历程回顾

系统架构演进历程回顾 在信息技术飞速发展的今天&#xff0c;系统架构的演进历程如同一部浓缩的科技史&#xff0c;从单机计算到分布式云原生&#xff0c;每一次变革都深刻影响着软件开发的范式。回顾这一历程&#xff0c;不仅能帮助我们理解技术演进的逻辑&#xff0c;也能为…

作者头像 李华
网站建设 2026/4/20 2:01:15

python terrascan

# 聊聊Python Terrascan&#xff1a;当IaC安全遇上Python的灵活 最近在基础设施即代码&#xff08;IaC&#xff09;安全扫描这个领域&#xff0c;有个工具逐渐引起了注意——Python Terrascan。它不是那种一夜爆红的技术&#xff0c;而是随着云原生和DevSecOps的普及&#xff0…

作者头像 李华