SpringBoot整合easyexcel,导入参数校验,批注导出
思路#
- 导入时,数据全部读取完,进行参数校验
- 如果参数校验失败后,将Excel导入的数据和校验错误信息,存到Redis中,最后将数据导出
添加依赖#
<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.6</version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.6</version> </dependency>
控制层#
一个导入,一个下载模板,一个导出批注后的模板
@Autowired RedisUtil redisUtil; @Autowired GsonBuilder gsonBuilder; private static final String PREFIX = "easyExcel_"; public static final String SEND_LIST = "sendList_"; public static final String SEND_LIST_ERROR = "_error"; public static final Long EXPIRE_TIME = 60 * 10L; @GetMapping(value = "importExcel") @ApiOperation("导入") public Result importExcel( @RequestParam(value = "file", required = true) MultipartFile file ) throws IOException { SendListListener sendListListener = new SendListListener(); EasyExcel.read(file.getInputStream(), SendListExcel.class, sendListListener).sheet().doRead(); List<SendListExcel> listExcels = sendListListener.getListExcels(); Gson gson = gsonBuilder.create(); if (sendListListener.getExcelErrorMap().size() > 0) { String uuid = IdUtils.id32(); String key = PREFIX + SEND_LIST + uuid; redisUtil.set(key, gson.toJson(listExcels), EXPIRE_TIME); redisUtil.set(key + SEND_LIST_ERROR, gson.toJson(sendListListener.getExcelErrorMap()), EXPIRE_TIME); return Result.error(uuid); } listExcels.forEach(System.out::println); return Result.success(); } @GetMapping("downloadExcelTemplate") @ApiOperation("下载Excel模板") public void export(HttpServletResponse response) throws Exception { response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); String fileName = "发送名单管理模板.xlsx"; response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")); EasyExcel.write(response.getOutputStream(), SendListExcel.class).sheet("sheet1").doWrite(new ArrayList<SendListExcel>()); } @GetMapping("downloadErrorExcel") @ApiImplicitParams(value = { @ApiImplicitParam(name = "uuid", dataType = "String", value = "校验参数失败后,返回的uuid") }) @ApiOperation("下载批注后到错误excel") public void downloadErrorExcel( HttpServletResponse response, @RequestParam(value = "uuid", required = true) String uuid ) throws IOException { SimpleDateFormat fDate = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); String fileName = fDate.format(new Date()) + ".xlsx"; response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx"); CommentWriteHandler commentWriteHandler = new CommentWriteHandler(); String key = PREFIX + SEND_LIST + uuid; String listExcelJson = (String) redisUtil.get(key); String listExcelErrorlJson = (String) redisUtil.get(key + SEND_LIST_ERROR); Gson gson = gsonBuilder.create(); if (listExcelJson != null && listExcelErrorlJson != null) { Type listExcelJsonType = new TypeToken<List<SendListExcel>>() { }.getType(); List<SendListExcel> sendListExcels = gson.fromJson(listExcelJson, listExcelJsonType); Type listExcelErrorlJsonType = new TypeToken<Map<Integer, List<ExcelError>>>() { }.getType(); Map<Integer, List<ExcelError>> errorMap = gson.fromJson(listExcelErrorlJson, listExcelErrorlJsonType); commentWriteHandler.setExcelErrorMap(errorMap); EasyExcel.write(response.getOutputStream(), SendListExcel.class) .inMemory(Boolean.TRUE) .sheet("sheet1") //注册批注拦截器 .registerWriteHandler(commentWriteHandler) .doWrite(sendListExcels); } }
导出实体类#
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ContentRowHeight; import com.alibaba.excel.annotation.write.style.HeadRowHeight; import lombok.Data; import java.io.Serializable; /** * @Description: * @Author:chenyanbin * @Date:2021/2/3 10:20 上午 * @Versiion:1.0 */ @Data @ExcelIgnoreUnannotated() @ContentRowHeight(10) @HeadRowHeight(20) public class SendListExcel implements Serializable { @ExcelProperty(value = "账号",index = 0) @ColumnWidth(20) private String account; @ExcelProperty(value = "模板编号",index = 1) @ColumnWidth(30) private String templateCode; @ExcelProperty(value = "类型",index = 2) @ColumnWidth(15) private String accountType; }
EasyExcel监听器#
import com.alibaba.excel.context.AnalysisContext; import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.List; /** * @Description: * @Author:chenyanbin * @Date:2021/2/3 10:40 上午 * @Versiion:1.0 */ public class SendListListener extends AnalysisEventListenerAdapter<SendListExcel> { private List<SendListExcel> listExcels = new ArrayList<>(); public SendListListener() { super(); listExcels.clear(); excelErrorMap.clear(); } /** * 每一条数据解析都会调用 */ @Override public void invoke(SendListExcel sendListExcel, AnalysisContext analysisContext) { listExcels.add(sendListExcel); } /** * 所有数据解析完成都会调用 */ @Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { SendListExcel sle = null; boolean isMatch = true; for (int i = 0; i < listExcels.size(); i++) { sle = listExcels.get(i); isMatch = true; Integer accountCellIndex = EasyExcelUtil.getCellIndex(sle, "account"); if (accountCellIndex != null) { if (StringUtils.isAllBlank(sle.getAccount())) { setExcelErrorMaps(i, accountCellIndex, "账号不能为空!"); } } Integer templateCodeCellIndex = EasyExcelUtil.getCellIndex(sle, "templateCode"); if (templateCodeCellIndex != null) { if (StringUtils.isAllBlank(sle.getTemplateCode())) { setExcelErrorMaps(i, templateCodeCellIndex, "模板编号不能为空!"); } } Integer accountTypeCellIndex = EasyExcelUtil.getCellIndex(sle, "accountType"); if (accountTypeCellIndex != null) { if (StringUtils.isAllBlank(sle.getAccountType())) { setExcelErrorMaps(i, accountTypeCellIndex, "类型不能为空!"); } else { if ("sms".equals(sle.getAccountType()) || "email".equals(sle.getAccountType()) || "wechat".equals(sle.getAccountType())) { isMatch = false; } if (isMatch) { setExcelErrorMaps(i, accountTypeCellIndex, "类型只允许:sms、email、wechat"); } } } } } public List<SendListExcel> getListExcels() { return listExcels; } }
其他#

import com.alibaba.excel.write.handler.AbstractRowWriteHandler; import org.apache.poi.ss.usermodel.*; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Description:AbstractRowWriteHandler适配器 * @Author:chenyanbin * @Date:2021/2/3 10:04 上午 * @Versiion:1.0 */ public abstract class AbstractRowWriteHandlerAdapter extends AbstractRowWriteHandler { protected Map<Integer, List<ExcelError>> excelErrorMap = new HashMap<>(); public void setExcelErrorMap(Map<Integer, List<ExcelError>> excelErrorMap) { this.excelErrorMap = excelErrorMap; } /** * 设置单元格批注 * @param sheet sheet * @param rowIndex 行索引 * @param colIndex 列索引 * @param value 批注 */ protected void setCellCommon(Sheet sheet, int rowIndex, int colIndex, String value) { Workbook workbook = sheet.getWorkbook(); CellStyle cellStyle = workbook.createCellStyle(); cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); cellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); Row row = sheet.getRow(rowIndex); if (row == null) { return; } Cell cell = row.getCell(colIndex); if (cell == null) { cell = row.createCell(colIndex); } if (value == null) { cell.removeCellComment(); return; } Drawing<?> drawing = sheet.createDrawingPatriarch(); CreationHelper factory = sheet.getWorkbook().getCreationHelper(); ClientAnchor anchor = factory.createClientAnchor(); Row row1 = sheet.getRow(anchor.getRow1()); if (row1 != null) { Cell cell1 = row1.getCell(anchor.getCol1()); if (cell1 != null) { cell1.removeCellComment(); } } Comment comment = drawing.createCellComment(anchor); RichTextString str = factory.createRichTextString(value); comment.setString(str); comment.setAuthor("admin"); cell.setCellComment(comment); cell.setCellStyle(cellStyle); } }

import com.alibaba.excel.event.AnalysisEventListener; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Description:AnalysisEventListener适配器 * @Author:chenyanbin * @Date:2021/2/3 10:09 上午 * @Versiion:1.0 */ public abstract class AnalysisEventListenerAdapter<T> extends AnalysisEventListener<T> { protected Map<Integer, List<ExcelError>> excelErrorMap = new HashMap<>(); public Map<Integer, List<ExcelError>> getExcelErrorMap() { return excelErrorMap; } /** * 设置批注集合 * * @param rowsNum 行数 * @param cellIndex 单元格索引 * @param msg 错误信息 */ protected void setExcelErrorMaps(int rowsNum, int cellIndex, String msg) { if (excelErrorMap.containsKey(rowsNum)) { List<ExcelError> excelErrors = excelErrorMap.get(rowsNum); excelErrors.add(new ExcelError(rowsNum, cellIndex, msg)); excelErrorMap.put(rowsNum, excelErrors); } else { List<ExcelError> excelErrors = new ArrayList<>(); excelErrors.add(new ExcelError(rowsNum, cellIndex, msg)); excelErrorMap.put(rowsNum, excelErrors); } } }

import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteTableHolder; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import java.util.List; /** * @Description:将参数校验失败的Exccel,添加批注后导出 * @Author:chenyanbin * @Date:2021/2/3 10:38 上午 * @Versiion:1.0 */ public class CommentWriteHandler extends AbstractRowWriteHandlerAdapter { @Override public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) { if (!isHead){ Sheet sheet = writeSheetHolder.getSheet(); if (excelErrorMap.containsKey(relativeRowIndex)) { List<ExcelError> excelErrors = excelErrorMap.get(relativeRowIndex); excelErrors.forEach(obj -> { setCellCommon(sheet, obj.getRow() + 1, obj.getColumn(), obj.getErrorMsg()); }); } } } }

import java.io.Serializable; /** * @Description:批注错误实体类 * @Author:chenyanbin * @Date:2021/2/3 10:05 上午 * @Versiion:1.0 */ public class ExcelError implements Serializable { /** 第几行 从1开始计数 */ private int row; /** 第几列 从1开始计数 */ private int column; /** 错误消息 */ private String errorMsg; public ExcelError(int row, int column, String errorMsg) { this.row = row; this.column = column; this.errorMsg = errorMsg; } public int getRow() { return row; } public int getColumn() { return column; } public String getErrorMsg() { return errorMsg; } @Override public String toString() { return "ExcelError{" + "row=" + row + ", column=" + column + ", errorMsg='" + errorMsg + '\'' + '}'; } }

import com.alibaba.excel.annotation.ExcelProperty; import lombok.extern.slf4j.Slf4j; import java.lang.reflect.Field; /** * @Description:EasyExcel工具类 * @Author:chenyanbin * @Date:2021/2/3 10:01 上午 * @Versiion:1.0 */ @Slf4j public class EasyExcelUtil { /** * 获取Excel单元格的索引 * * @param obj JavaBean对象 * @param fieldValue JavaBean字段值 * @return */ public static Integer getCellIndex(Object obj, String fieldValue) { try { Field declaredField = obj.getClass().getDeclaredField(fieldValue); ExcelProperty annotation = declaredField.getAnnotation(ExcelProperty.class); if (annotation == null) { return null; } return annotation.index(); } catch (NoSuchFieldException e) { log.error("error:", e); } return null; } }
功能演示#
分类:
Spring Boot
, Easy Excel
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?