直接看代碼。根據個人需要做改動
注:POI也可以做批注,文章鏈接https://www.cnblogs.com/qq1445496485/p/15622664.html

/** * 導出(批注) * * @param response */ @GetMapping("/exportComment") public void exportComment(HttpServletResponse response) { try { WriteCellStyle headWriteCellStyle = new WriteCellStyle(); // 設置背景顏色 headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex()); // 設置頭字體 WriteFont headWriteFont = new WriteFont(); headWriteFont.setFontHeightInPoints((short)14); // 字體加粗 headWriteFont.setBold(true); headWriteCellStyle.setWriteFont(headWriteFont); // 設置頭居中 headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); // 內容策略 WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); // 設置內容字體 WriteFont contentWriteFont = new WriteFont(); contentWriteFont.setFontHeightInPoints((short)12); contentWriteFont.setFontName("宋體"); contentWriteCellStyle.setWriteFont(contentWriteFont); // 設置 水平居中 contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); // 設置 垂直居中 contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 設置單元格格式為 文本 contentWriteCellStyle.setDataFormat((short)49); HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); // 生成表格數據 List<List<Object>> dataList = new ArrayList<>(); dataList.add(new ArrayList<>(Arrays.asList(new Object[] {"表頭11", "表頭2", "表頭3", "表頭4"}))); dataList.add(new ArrayList<>(Arrays.asList(new Object[] {"表頭1", "表頭2", "表頭3", "表頭4"}))); dataList.add(new ArrayList<>(Arrays.asList(new Object[] {"表頭31", "表頭2", "表頭3", "表頭4"}))); // 導出文件 String fileName = new String("文件名稱.xlsx".getBytes(), "UTF-8"); String sheetName = "模板"; List<Map<String, String>> commentList = new ArrayList<>(); commentList.add(CommentWriteHandler.createCommentMap(sheetName, 0, 0, "A批注。")); commentList.add(CommentWriteHandler.createCommentMap(sheetName, 0, 1, "B批注。")); commentList.add(CommentWriteHandler.createCommentMap(sheetName, 2, 0, "B批注。")); response.addHeader("Content-Disposition", "filename=" + fileName); // 設置類型,擴展名為.xls response.setContentType("application/vnd.ms-excel"); ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()) .inMemory(Boolean.TRUE) .registerWriteHandler(new CommentWriteHandler(commentList, "xlsx")) .registerWriteHandler(horizontalCellStyleStrategy) .build(); WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build(); excelWriter.write(dataList, writeSheet); // 千萬別忘記finish 會幫忙關閉流 excelWriter.finish(); } catch (Exception e) { e.printStackTrace(); } }

package com.temporary.handle; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.excel.write.handler.AbstractRowWriteHandler; import com.alibaba.excel.write.metadata.holder.*; import org.apache.poi.hssf.usermodel.*; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * @author Han * @Description 自定義批注處理器 * @date 2022/3/21 */ public class CommentWriteHandler extends AbstractRowWriteHandler { /** * sheet名稱KEY */ public static final String SHEETNAME_NAME = "sheetName"; /** * 文檔后綴名 */ private String extension; /** * 列索引key */ public static final String COLINDEX_NAME = "colIndex"; /** * 行索引key */ public static final String ROWINDEX_NAME = "rowIndex"; /** * 批注內容key */ public static final String COMMENTCONTENT_NAME = "commentContent"; /** * sheet頁名稱列表 */ private List<String> sheetNameList; List<Map<String, String>> commentList = new ArrayList<>(); public CommentWriteHandler(List<Map<String, String>> commentList, String extension) { this.commentList = commentList != null && commentList.size() > 0 ? commentList.stream() .filter(x -> x.keySet().contains(SHEETNAME_NAME) == true && x.get(SHEETNAME_NAME) != null && StrUtil.isNotBlank(x.get(SHEETNAME_NAME).toString()) && x.keySet().contains(COLINDEX_NAME) == true && x.get(COLINDEX_NAME) != null && StrUtil.isNotBlank(x.get(COLINDEX_NAME).toString()) && x.keySet().contains(ROWINDEX_NAME) == true && x.get(ROWINDEX_NAME) != null && StrUtil.isNotBlank(x.get(ROWINDEX_NAME).toString()) && x.keySet().contains(COMMENTCONTENT_NAME) == true && x.get(COMMENTCONTENT_NAME) != null && StrUtil.isNotBlank(x.get(COMMENTCONTENT_NAME).toString())) .collect(Collectors.toList()) : new ArrayList<>(); sheetNameList = this.commentList.stream().map(x -> x.get(SHEETNAME_NAME).toString()).collect(Collectors.toList()); this.extension = extension; } /** * 生成批注信息 * * @param sheetName sheet頁名稱 * @param rowIndex 行號 * @param columnIndex 列號 * @param commentContent 批注內容 * @return */ public static Map<String, String> createCommentMap(String sheetName, int rowIndex, int columnIndex, String commentContent) { Map<String, String> map = new HashMap<>(); // sheet頁名稱 map.put(SHEETNAME_NAME, sheetName); // 行號 map.put(ROWINDEX_NAME, rowIndex + ""); // 列號 map.put(COLINDEX_NAME, columnIndex + ""); // 批注內容 map.put(COMMENTCONTENT_NAME, commentContent); return map; } @Override public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) { Sheet sheet = writeSheetHolder.getSheet(); // 不需要添加批注,或者當前sheet頁不需要添加批注 if (commentList == null || commentList.size() <= 0 || sheetNameList.contains(sheet.getSheetName()) == false) { return; } // 獲取當前行的批注信息 List<Map<String, String>> rowCommentList = commentList.stream() .filter(x -> StrUtil.equals(x.get(SHEETNAME_NAME).toString(), sheet.getSheetName()) && relativeRowIndex == Integer.parseInt(x.get(ROWINDEX_NAME))) .collect(Collectors.toList()); // 當前行沒有批注信息 if (rowCommentList == null || rowCommentList.size() <= 0) { return; } List<String> colIndexList = rowCommentList.stream().map(x -> x.get(COLINDEX_NAME)).distinct().collect(Collectors.toList()); for (String colIndex : colIndexList) { // 同一單元格的批注信息 List<Map<String, String>> cellCommentList = rowCommentList.stream() .filter(x -> StrUtil.equals(colIndex, x.get(COLINDEX_NAME))) .collect(Collectors.toList()); if (CollectionUtil.isEmpty(cellCommentList)) { continue; } // 批注內容拼成一條 String commentContent = cellCommentList.stream().map(x -> x.get(COMMENTCONTENT_NAME)).collect(Collectors.joining()); Cell cell = row.getCell(Integer.parseInt(colIndex)); addComment(cell, commentContent, extension); } // 刪除批注信息 commentList.remove(rowCommentList); // 重新獲取要添加的sheet頁姓名 sheetNameList = commentList.stream().map(x -> x.get(SHEETNAME_NAME).toString()).collect(Collectors.toList()); } /** * 給Cell添加批注 * * @param cell 單元格 * @param value 批注內容 * @param extension 擴展名 */ public static void addComment(Cell cell, String value, String extension) { Sheet sheet = cell.getSheet(); cell.removeCellComment(); if ("xls".equals(extension)) { ClientAnchor anchor = new HSSFClientAnchor(); // 關鍵修改 anchor.setDx1(0); anchor.setDx2(0); anchor.setDy1(0); anchor.setDy2(0); anchor.setCol1(cell.getColumnIndex()); anchor.setRow1(cell.getRowIndex()); anchor.setCol2(cell.getColumnIndex() + 5); anchor.setRow2(cell.getRowIndex() + 6); // 結束 Drawing drawing = sheet.createDrawingPatriarch(); Comment comment = drawing.createCellComment(anchor); // 輸入批注信息 comment.setString(new HSSFRichTextString(value)); // 將批注添加到單元格對象中 cell.setCellComment(comment); } else if ("xlsx".equals(extension)) { ClientAnchor anchor = new XSSFClientAnchor(); // 關鍵修改 anchor.setDx1(0); anchor.setDx2(0); anchor.setDy1(0); anchor.setDy2(0); anchor.setCol1(cell.getColumnIndex()); anchor.setRow1(cell.getRowIndex()); anchor.setCol2(cell.getColumnIndex() + 5); anchor.setRow2(cell.getRowIndex() + 6); // 結束 Drawing drawing = sheet.createDrawingPatriarch(); Comment comment = drawing.createCellComment(anchor); // 輸入批注信息 comment.setString(new XSSFRichTextString(value)); // 將批注添加到單元格對象中 cell.setCellComment(comment); } } }