news 2026/5/6 4:56:35

别再手动处理Excel了!用EasyExcel 2.2.3 + Spring Boot 5分钟搞定数据导入导出(附完整代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再手动处理Excel了!用EasyExcel 2.2.3 + Spring Boot 5分钟搞定数据导入导出(附完整代码)

Spring Boot + EasyExcel 极简数据导入导出实战指南

在后台管理系统开发中,Excel导入导出是刚需功能。传统POI方案代码臃肿且内存消耗大,而阿里开源的EasyExcel以简洁API和低内存占用著称。本文将手把手带你在Spring Boot中5分钟实现企业级Excel处理能力。

1. 环境准备与项目初始化

首先创建一个基础的Spring Boot项目,添加以下核心依赖:

<dependencies> <!-- Spring Boot Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- EasyExcel核心库 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.1.1</version> </dependency> <!-- Lombok简化代码 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>

推荐使用最新稳定版EasyExcel 3.x系列,相比2.x版本在性能和功能上都有显著提升。初始化后的项目结构应包含:

src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── example/ │ │ ├── controller/ │ │ ├── model/ │ │ └── Application.java │ └── resources/ │ └── application.yml

2. 数据模型定义与注解解析

定义用户数据实体类,通过注解配置Excel映射关系:

@Data @Builder @NoArgsConstructor @AllArgsConstructor public class UserExportVO { @ExcelProperty(value = "用户ID", index = 0) private Long id; @ExcelProperty(value = "用户名", index = 1) private String username; @ExcelProperty(value = "手机号", index = 2) private String mobile; @ExcelProperty(value = "注册时间", index = 3) @DateTimeFormat("yyyy-MM-dd HH:mm:ss") private Date createTime; @ExcelProperty(value = "账户状态", index = 4) private String status; // 自定义转换器示例 @ExcelProperty(value = "会员等级", index = 5, converter = LevelConverter.class) private Integer level; }

关键注解说明:

注解作用常用属性
@ExcelProperty定义字段映射value: 列名, index: 列顺序
@DateTimeFormat日期格式化value: 格式字符串
@NumberFormat数字格式化value: 格式模式
@ColumnWidth列宽设置value: 字符宽度

自定义枚举转换器实现:

public class LevelConverter implements Converter<Integer> { @Override public Class<?> supportJavaTypeKey() { return Integer.class; } @Override public CellDataTypeEnum supportExcelTypeKey() { return CellDataTypeEnum.STRING; } @Override public Integer convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { return switch (cellData.getStringValue()) { case "普通会员" -> 1; case "黄金会员" -> 2; case "铂金会员" -> 3; default -> 0; }; } @Override public CellData<String> convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { return new CellData<>(switch (value) { case 1 -> "普通会员"; case 2 -> "黄金会员"; case 3 -> "铂金会员"; default -> "非会员"; }); } }

3. 导出功能实现

创建ExportController处理导出请求:

@RestController @RequestMapping("/api/excel") public class ExcelExportController { @GetMapping("/export/users") public void exportUserList(HttpServletResponse response) throws IOException { // 1. 准备数据(实际项目从数据库查询) List<UserExportVO> dataList = mockUserData(); // 2. 设置响应头 String fileName = URLEncoder.encode("用户列表", "UTF-8"); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx"); // 3. 执行导出 EasyExcel.write(response.getOutputStream(), UserExportVO.class) .sheet("用户数据") .doWrite(dataList); } private List<UserExportVO> mockUserData() { List<UserExportVO> list = new ArrayList<>(); // 模拟20条数据 for (int i = 1; i <= 20; i++) { list.add(UserExportVO.builder() .id(10000L + i) .username("user_" + i) .mobile("188" + String.format("%08d", i)) .createTime(new Date()) .status(i % 3 == 0 ? "禁用" : "正常") .level(i % 4) .build()); } return list; } }

高级导出技巧:

  1. 动态列控制:通过excludeColumnFieldNamesincludeColumnFieldNames方法
  2. 多Sheet导出:创建多个WriteSheet对象
  3. 模板导出:使用withTemplate()方法基于模板填充
// 动态列示例 Set<String> excludeColumns = new HashSet<>(); excludeColumns.add("mobile"); excludeColumns.add("level"); EasyExcel.write(response.getOutputStream(), UserExportVO.class) .excludeColumnFiledNames(excludeColumns) .sheet("精简用户数据") .doWrite(dataList);

4. 导入功能实现

创建ImportController处理文件上传:

@PostMapping("/import/users") public ApiResult importUsers(@RequestParam("file") MultipartFile file) { try { List<UserImportVO> importData = EasyExcel.read(file.getInputStream()) .head(UserImportVO.class) .sheet() .doReadSync(); // 业务处理(如数据校验、入库等) processImportData(importData); return ApiResult.success("导入成功", importData.size()); } catch (Exception e) { return ApiResult.fail("导入失败: " + e.getMessage()); } } // 带监听器的读取方式(适合大数据量) @PostMapping("/import/users-batch") public void importUsersBatch(@RequestParam("file") MultipartFile file) { EasyExcel.read(file.getInputStream(), UserImportVO.class, new UserDataListener(userService)) .sheet() .doRead(); }

创建数据监听器实现增量处理:

public class UserDataListener extends AnalysisEventListener<UserImportVO> { private static final int BATCH_SIZE = 100; private final UserService userService; private List<UserImportVO> cachedList = new ArrayList<>(BATCH_SIZE); public UserDataListener(UserService userService) { this.userService = userService; } @Override public void invoke(UserImportVO data, AnalysisContext context) { cachedList.add(data); if (cachedList.size() >= BATCH_SIZE) { saveData(); cachedList.clear(); } } @Override public void doAfterAllAnalysed(AnalysisContext context) { if (!cachedList.isEmpty()) { saveData(); } } private void saveData() { userService.batchProcess(cachedList); } }

导入验证技巧:

  1. 使用@ExcelPropertyindex属性确保列顺序
  2. 在监听器中实现业务校验逻辑
  3. 使用@DateTimeFormat@NumberFormat保证数据格式

5. 高级功能与性能优化

5.1 样式定制

通过注解自定义单元格样式:

@Data @HeadStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 22) // 表头背景色 @HeadFontStyle(fontHeightInPoints = 12, bold = true) // 表头字体 @ContentStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 40) // 内容背景色 @ContentFontStyle(fontHeightInPoints = 11) // 内容字体 public class StyledExportVO { @ColumnWidth(20) @ExcelProperty("订单编号") private String orderNo; @ContentStyle(fillForegroundColor = 42) @ExcelProperty("异常标记") private String errorFlag; }

5.2 大数据量处理

对于10万+数据量的导出,建议:

  1. 使用分页查询避免OOM
  2. 启用web响应压缩
  3. 添加进度提示
// 分页导出示例 public void largeDataExport(HttpServletResponse response) throws IOException { response.setHeader("Content-Encoding", "gzip"); try (OutputStream out = new GZIPOutputStream(response.getOutputStream()); ExcelWriter excelWriter = EasyExcel.write(out, LargeDataVO.class).build()) { WriteSheet writeSheet = EasyExcel.writerSheet("大数据").build(); int pageSize = 5000; int pageNo = 1; while (true) { Page<LargeDataVO> page = dataService.getByPage(pageNo, pageSize); if (page.getRecords().isEmpty()) break; excelWriter.write(page.getRecords(), writeSheet); pageNo++; } } }

5.3 常见问题解决方案

  1. 日期格式问题

    • 确保实体类字段使用java.util.Date
    • 注解格式与Excel实际格式一致
  2. 数字精度丢失

    • 对于大数字使用String类型接收
    • 使用@NumberFormat指定精度
  3. 内存溢出处理

    // 读取配置 ExcelReaderBuilder readerBuilder = EasyExcel.read() .autoCloseStream(true) .autoTrim(true) .ignoreEmptyRow(true);
  4. 浏览器兼容性

    • 设置正确的Content-Type
    • 文件名进行URL编码

6. 实战:完整用户管理系统案例

整合导入导出的完整Controller示例:

@RestController @RequestMapping("/user") public class UserController { @PostMapping("/import") public ApiResult importUsers(@RequestParam MultipartFile file) { try { List<UserImportDTO> list = EasyExcel.read(file.getInputStream()) .head(UserImportDTO.class) .registerReadListener(new UserImportListener()) .sheet() .doReadSync(); return ApiResult.success("导入成功", list.size()); } catch (Exception e) { return ApiResult.fail(e.getMessage()); } } @GetMapping("/export") public void exportUsers(UserQuery query, HttpServletResponse response) throws IOException { response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); String fileName = URLEncoder.encode("用户列表_" + System.currentTimeMillis(), "UTF-8"); response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx"); List<UserExportVO> data = userService.exportUsers(query); EasyExcel.write(response.getOutputStream(), UserExportVO.class) .registerConverter(new LocalDateTimeConverter()) .sheet("用户数据") .doWrite(data); } }

前端Vue调用示例:

// 导出 function exportExcel(params) { return axios({ url: '/user/export', method: 'get', params, responseType: 'blob' }).then(res => { const blob = new Blob([res.data], { type: res.headers['content-type'] }) const link = document.createElement('a') link.href = URL.createObjectURL(blob) link.download = decodeURIComponent( res.headers['content-disposition'].split('filename=')[1] ) document.body.appendChild(link) link.click() document.body.removeChild(link) }) } // 导入 function importExcel(file) { const formData = new FormData() formData.append('file', file) return axios.post('/user/import', formData, { headers: { 'Content-Type': 'multipart/form-data' } }) }

在微服务架构中,建议将Excel处理封装为独立服务,通过Feign客户端调用。对于超大规模数据(100万+),可以考虑以下优化方案:

  1. 采用分片导出策略
  2. 使用消息队列异步处理
  3. 实现断点续传功能
  4. 提供OSS直传方案

实际项目中遇到的典型问题包括:合并单元格处理、动态表头生成、多级表头设计等。针对这些需求,EasyExcel提供了@ExcelMerge等注解支持,也可以通过自定义合并策略实现复杂布局。

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

蓝桥杯单片机备赛:手把手教你用PCF8591玩转AD/DA转换(附官方驱动避坑)

蓝桥杯单片机备赛实战&#xff1a;PCF8591模块AD/DA转换全流程解析与避坑指南 第一次接触PCF8591模块时&#xff0c;我被它既能读取模拟信号又能输出模拟电压的特性所吸引。但在实际调试过程中&#xff0c;光敏电阻读数不稳定、DA输出电压偏差等问题接踵而至。更让人头疼的是&a…

作者头像 李华
网站建设 2026/5/6 4:55:37

HMC5883L电子罗盘校准避坑指南:硬磁、软磁干扰与椭圆拟合实战

HMC5883L电子罗盘校准避坑指南&#xff1a;硬磁、软磁干扰与椭圆拟合实战 当你将HMC5883L电子罗盘集成到无人机飞控系统中&#xff0c;满怀期待地启动设备&#xff0c;却发现指南针读数在30度范围内随机跳动——这种场景对嵌入式开发者来说再熟悉不过。磁力计校准是导航系统开发…

作者头像 李华
网站建设 2026/5/6 4:54:26

从A2L到Hex:Vector CANape离线标定全流程详解与避坑指南

从A2L到Hex&#xff1a;Vector CANape离线标定全流程详解与避坑指南 在汽车电子控制单元&#xff08;ECU&#xff09;开发中&#xff0c;标定工程师经常面临一个关键挑战&#xff1a;如何将台架测试中优化的参数安全可靠地固化到ECU中。Vector CANape作为行业标杆工具&#xff…

作者头像 李华
网站建设 2026/5/6 4:53:27

利用快马AI平台基于Flask镜像快速构建个人博客系统原型

今天想和大家分享一个快速搭建个人博客系统的实践过程。作为一个Python爱好者&#xff0c;我选择了Flask框架作为基础&#xff0c;借助InsCode(快马)平台的AI能力&#xff0c;整个过程比想象中顺利很多。 项目初始化 在快马平台选择Flask镜像作为基础模板&#xff0c;这个镜像已…

作者头像 李华