一、环境准备
1. 添加依赖
<!-- EasyExcel核心依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>
<!-- Spring Web文件处理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 数据校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
二、基础功能实现
1. 数据模型定义
@Data
public class UserExportVO {
@ExcelProperty("用户ID")
private Long id;
@ExcelProperty("用户名")
private String username;
@ExcelProperty(value = "创建时间", format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@ExcelProperty("状态")
@ExcelConverterEnum(codeProperty = "code", descProperty = "desc")
private UserStatus status;
}
@Data
public class UserImportDTO {
@ExcelProperty("用户名")
@NotBlank(message = "用户名不能为空")
private String username;
@ExcelProperty("邮箱")
@Email(message = "邮箱格式不正确")
private String email;
@ExcelProperty("手机号")
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式错误")
private String phone;
}
public enum UserStatus {
ACTIVE(1, "激活"),
DISABLED(0, "禁用");
@EnumValue
private final Integer code;
private final String desc;
}
2. Excel导出实现
@RestController
@RequestMapping("/api/excel")
public class ExcelController {
@GetMapping("/export/users")
public void exportUserList(HttpServletResponse response) throws IOException {
// 1. 设置响应头
String fileName = URLEncoder.encode("用户列表.xlsx", StandardCharsets.UTF_8);
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
// 2. 查询数据(示例)
List<UserExportVO> dataList = userService.getExportData();
// 3. 写入Excel
EasyExcel.write(response.getOutputStream(), UserExportVO.class)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 自动列宽
.sheet("用户数据")
.doWrite(dataList);
}
}
3. Excel导入实现
@PostMapping("/import/users")
public ApiResult<?> importUsers(@RequestParam("file") MultipartFile file) {
try {
// 1. 创建监听器
UserImportListener listener = new UserImportListener(userService);
// 2. 读取Excel
EasyExcel.read(file.getInputStream(), UserImportDTO.class, listener)
.sheet()
.headRowNumber(1) // 跳过标题行
.doRead();
// 3. 返回处理结果
return ApiResult.success("导入成功", listener.getResult());
} catch (Exception e) {
return ApiResult.error("导入失败:" + e.getMessage());
}
}
// 自定义读取监听器
public class UserImportListener extends AnalysisEventListener<UserImportDTO> {
private final UserService userService;
private final List<User> successList = new ArrayList<>();
private final List<ImportError> errorList = new ArrayList<>();
public UserImportListener(UserService userService) {
this.userService = userService;
}
@Override
public void invoke(UserImportDTO data, AnalysisContext context) {
// 数据校验
Set<ConstraintViolation<UserImportDTO>> violations = Validation.buildDefaultValidatorFactory()
.getValidator()
.validate(data);
if (!violations.isEmpty()) {
errorList.add(new ImportError(
context.readRowHolder().getRowIndex(),
violations.stream()
.map(ConstraintViolation::getMessage)
.collect(Collectors.joining(";"))
);
return;
}
// 转换为实体并保存
try {
User user = convertToEntity(data);
successList.add(user);
// 每100条批量插入
if (successList.size() >= 100) {
userService.batchInsert(successList);
successList.clear();
}
} catch (Exception e) {
errorList.add(new ImportError(
context.readRowHolder().getRowIndex(),
e.getMessage()
));
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 处理剩余数据
if (!successList.isEmpty()) {
userService.batchInsert(successList);
}
}
}
三、高级功能扩展
1. 动态表头导出
public void dynamicExport(HttpServletResponse response) {
// 1. 构建动态表头
List<List<String>> head = new ArrayList<>();
head.add(Collections.singletonList("基础信息"));
head.add(Arrays.asList("姓名", "年龄"));
head.add(Arrays.asList("联系方式", "手机号", "邮箱"));
// 2. 构建数据
List<List<Object>> data = List.of(
List.of("张三", 25, "13800138000", "zhangsan@example.com"),
List.of("李四", 30, "13900139000", "lisi@example.com")
);
// 3. 写入Excel
EasyExcel.write(response.getOutputStream())
.head(head)
.sheet("动态表头")
.doWrite(data);
}
2. 百万数据导出优化
public void largeDataExport(HttpServletResponse response) {
// 1. 分页查询参数
int pageSize = 5000;
int totalPage = calculateTotalPage();
// 2. 分批次写入
try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.build()) {
WriteSheet writeSheet = EasyExcel.writerSheet("大数据量").head(UserExportVO.class).build();
for (int page = 0; page < totalPage; page++) {
List<UserExportVO> data = userService.getBatchData(page, pageSize);
excelWriter.write(data, writeSheet);
}
}
}
3. 模板下载与数据校验
@GetMapping("/template")
public void downloadTemplate(HttpServletResponse response) throws IOException {
String fileName = URLEncoder.encode("用户导入模板.xlsx", StandardCharsets.UTF_8);
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
// 创建带下拉列表的模板
ExcelWriter writer = EasyExcel.write(response.getOutputStream())
.head(UserImportDTO.class)
.registerWriteHandler(new SelectSheetWriteHandler(
Collections.singletonMap("状态", Arrays.asList("激活", "禁用"))
))
.build();
writer.write(Collections.emptyList(), EasyExcel.writerSheet("模板").build());
writer.finish();
}
四、配置优化建议
1. 文件上传限制配置
spring:
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
2. 全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ApiResult<?> handleException(Exception e) {
if (e instanceof ExcelAnalysisException) {
return ApiResult.error("Excel解析失败:" + e.getMessage());
}
return ApiResult.error("系统异常:" + e.getMessage());
}
}
3. 导出样式定制
public class CustomCellStyleStrategy extends AbstractCellStyleStrategy {
@Override
protected void setHeadCellStyle(Cell cell, Head head, Integer relativeRowIndex) {
CellStyle style = cell.getSheet().getWorkbook().createCellStyle();
Font font = cell.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);
}
}
五、常见问题解决方案
-
中文乱码问题:
response.setCharacterEncoding("UTF-8"); response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + fileName);
-
内存溢出处理:
-
使用
SXSSFWorkbook
模式 -
增加JVM参数:
-Xms512m -Xmx2048m
-
-
复杂表头合并:
.registerWriteHandler(new CellRangeStrategy(0, 0, 0, 3)) // 合并第一行前四列
六、最佳实践建议
-
数据校验:
-
前端校验:文件类型、大小
-
后端校验:字段格式、业务规则
-
数据库校验:唯一性约束
-
-
性能优化:
-
使用分页查询避免内存溢出
-
开启批处理模式(JDBC Batch)
-
异步导出(生成文件后提供下载链接)
-
-
安全防护:
-
文件类型白名单校验
-
病毒扫描
-
权限控制(导出范围限制)
-
-
用户体验优化:
-
进度提示(WebSocket推送进度)
-
错误报告生成(导出错误行信息)
-
断点续传(大文件分片上传)
-
推荐结合阿里云OSS实现云端文件存储,提升大文件处理能力。对于复杂业务场景,可考虑集成Apache POI进行更底层的Excel操作。