记一个带批注、表头样式的导入导出excel方法(基于easyexcel)

技术栈:easyexcel-2.2.10,poi-4.1.2,lombok,hutool-5.8.19;公司自用导入导出方法,可能不是那么的优雅,但胜在稳定实用。

java 复制代码
/**
 * @Author 955
 * @Date 2023-10-10 11:52
 * @Description 错误批注信息对象
 */
@Data
public class ErrorTypeNewVo implements Serializable {

    public ErrorTypeNewVo(String name, String message) {
        this.name = name;
        this.errors = message;
    }

    public ErrorTypeNewVo(String message, String name, int rownum, int cellnum) {
        this.name = name;
        this.errors = message;
        this.lineNum = rownum;
        this.cellnum = cellnum;
    }

    private String name;
    private String errors;

    private int lineNum;
    private int cellnum;

}

一些要用到的工具类或方法

java 复制代码
    /**
     * 取得cell内的值
     *
     * @param cell
     * @return
     */
    public static String getStrByCell(Cell cell) {

        if (cell == null)
            return null;
        String temp = null;

        switch (cell.getCellType()) {
            case BLANK:
            case STRING:
                temp = cell.getStringCellValue();
                break;
            case FORMULA:
                try {
                    temp = cell.getStringCellValue();
                } catch (IllegalStateException e) {
                    temp = cell.getNumericCellValue() + "";
                }

                break;
            case NUMERIC:
                boolean remar = false;
                try {
                    remar = DateUtil.isCellDateFormatted(cell);
                } catch (Exception e) {
                    // TODO: handle exception
                }
                if (remar) {
                    double d = cell.getNumericCellValue();
                    Date date = DateUtil.getJavaDate(d);
                    SimpleDateFormat a = new SimpleDateFormat("yyyy-MM-dd");
                    temp = a.format(date);
                } else {
                    temp = String.valueOf(cell.getNumericCellValue());
                }
                break;
            default:
                temp = cell.getStringCellValue();
                break;
        }

        if (temp.trim().equals(""))
            temp = null;
        else
            temp = temp.trim();

        return temp;
    }

    /**
     * 设置单元格样式
     *
     * @param workbook
     * @param cell
     * @param col
     * @param fg
     */
    public void setStyle(XSSFWorkbook workbook, Cell cell, String col, XSSFColor fg) {
        XSSFCellStyle style = workbook.createCellStyle();

        style.setFillForegroundColor(fg);
        // style.setFillPattern(CellStyle.SOLID_FOREGROUND);

        cell.setCellStyle(style);

        cell.setCellValue(col);
    }

   /**
     * 表头样式
     *
     * @return
     */
    public static WriteCellStyle getHeadWriteCellStyle() {
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.index);
        headWriteCellStyle.setBottomBorderColor(IndexedColors.BLACK.index);
        headWriteCellStyle.setLeftBorderColor(IndexedColors.BLACK.index);
        headWriteCellStyle.setRightBorderColor(IndexedColors.BLACK.index);
        headWriteCellStyle.setTopBorderColor(IndexedColors.BLACK.index);
        return headWriteCellStyle;
    }

    /**
     * 内容样式
     *
     * @return
     */
    public static WriteCellStyle getContentWriteCellStyle() {
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        contentWriteCellStyle.setWrapped(true);
//        contentWriteCellStyle.setFillForegroundColor(IndexedColors.LEMON_CHIFFON.index);
//        contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
//        contentWriteCellStyle.setWriteFont();
        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        return contentWriteCellStyle;
    }
    
    /**
     * 设置表头数据
     *
     * @return
     */
    private List<List<String>> head(List<String> titleName) {
        List<List<String>> list = new ArrayList<List<String>>();
        if (CollUtil.isNotEmpty(titleName)) {
            for (String s : titleName) {
                List<String> head = new ArrayList<String>();
                head.add(s);
                list.add(head);
            }
        }
        return list;
    }
    
	/**
	 * @content 得到不同的时间类型
	 * @author liuxd
	 * @time 2011/07/24 22:00
	 */
	public static String getNow(int type) {
		SimpleDateFormat sdf = null;
		switch (type) {
		case 0:
			sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
			break;
		case 1:
			sdf = new SimpleDateFormat("yyyyMMdd");
			break;
		case 2:
			sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			break;
		case 3:
			sdf = new SimpleDateFormat("yyyyMM");
			break;
		case 4:
			sdf = new SimpleDateFormat("yyyy年MM月dd日");
			break;
		case 5:
			sdf = new SimpleDateFormat("yyyy-MM-dd");
			break;
		case 6:
			sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); //取到毫秒
			break;
		case 7:
			sdf = new SimpleDateFormat("yyyyMMddHHmmss"); //取到秒
			break;
		default:
			sdf = new SimpleDateFormat("yyyy-M-D");
			break;
		}

		return sdf.format(new Date());
	}

导出方法体

java 复制代码
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.AbstractHeadColumnWidthStyleStrategy;
import lombok.AllArgsConstructor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.springframework.stereotype.Service;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

    private static int index = 500000;
        /**
     * 
     * @param titleName sheet名称
     * @param tittleNames 标题集合
     * @param vos 内容详情集合
     * @param errorListVo 错误批注集合
     * @return
     */
    public String newCreateExcelTemple_Object(String titleName, List<String> tittleNames, List<List<Object>> vos, List<ErrorTypeNewVo> errorListVo) {
        String fileName = OrgTools.getNow(6) + ".xlsx";
        //本地文件名随意取
        String locaPath = path + fileName;
        OutputStream out = null;
        try {
            out = new FileOutputStream(locaPath);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        try {
            // 设置数据集合
            List<List<String>> dataList = new ArrayList<List<String>>();
            if (CollUtil.isNotEmpty(vos)) {
                for (List<Object> objects : vos) {
                    List<String> strs = new ArrayList<>();
                    for (Object obj : objects) {
                        String str = null;
                        if (obj != null) {
                            if (obj instanceof Date) {
                                SimpleDateFormat ff = new SimpleDateFormat("yyyy-MM-dd");
                                str = ff.format(obj);
                            } else {
                                str = obj.toString();
                            }
                        }
                        strs.add(str);
                    }
                    dataList.add(strs);
                }
                ExcelWriterBuilder writerBuilder = EasyExcel.write();
                writerBuilder.file(out);
                writerBuilder.excelType(ExcelTypeEnum.XLSX);
                writerBuilder.head(head(tittleNames));
                ExcelWriter excelWriter = writerBuilder.build();
                // 写第一个sheet, sheet1 数据全是List<String> 无模型映射关系
                long total = dataList.size();
                long count = total / 500000;
                if (total % 500000 > 0) {
                    count++;
                }
                WriteCellStyle headWriteCellStyle = getHeadWriteCellStyle();
                WriteCellStyle contentWriteCellStyle = getContentWriteCellStyle();
                //这个策略是 头是头的样式 内容是内容的样式
                HorizontalCellStyleStrategy horizontalCellStyleStrategy =
                        new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
                for (int i = 0; i < count; i++) {
                    List<List<String>> temp = new ArrayList<List<String>>();
                    if (i == count - 1) {
                        temp = dataList.subList(i * index, dataList.size());
                    } else {
                        temp = dataList.subList(i * index, (i + 1) * index);
                    }
                    WriteSheet sheet1 = EasyExcel.writerSheet(i, titleName + i).registerWriteHandler(horizontalCellStyleStrategy).registerWriteHandler(new AbstractHeadColumnWidthStyleStrategy() {
                        @Override
                        protected Integer columnWidth(Head head, Integer columnIndex) {
                            if (head == null) {
                                return 17;
                            }
                            // 简单根据列头名称长度进行调整列的宽度
                            // 当遍历完所有指定的head列头后
                            // 第一行数据的第一列也会进入到这个逻辑, 此时head 为 null
                            return head.getHeadNameList().get(0).length() * 10;
                        }
                    }).build();
                    excelWriter.write(temp, sheet1);
                }
                excelWriter.finish();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(locaPath);
            XSSFWorkbook wb = new XSSFWorkbook(inputStream);
            XSSFSheet sheet = wb.getSheetAt(0);
            for (ErrorTypeNewVo errorTypevo : errorListVo) {
                Row row = sheet.getRow(errorTypevo.getLineNum());
                Cell cell = row.getCell(errorTypevo.getCellnum());
                if (cell == null) {
                    cell = row.getCell(errorTypevo.getCellnum() - 1);
                }
                String name = errorTypevo.getName();
                String messageAll = errorTypevo.getErrors();
                String message = "";
                if (StrUtil.isNotEmpty(name)) {
                    message += "错误类型:" + name;
                }
                if (StrUtil.isNotEmpty(messageAll)) {
                    message += ",详细错误:" + messageAll;
                }
                String t1 = "";
                try {
                    t1 = getStrByCell(cell);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                XSSFColor color = new XSSFColor(java.awt.Color.red);
                if (t1 != null) {
                    setStyle(wb, cell, t1, color);
                } else {
                    t1 = "";
                    if (StrUtil.isNotEmpty(name)) {
                        t1 += "错误类型:" + name;
                    }
                    if (StrUtil.isNotEmpty(messageAll)) {
                        t1 += ",详细错误:" + messageAll;
                    }
                    setStyle(wb, cell, t1, color);
                }
                if (cell != null) {
                //设置批注:能解决Multiple cell comments in one cell are not allowed, cell: xx问题
                    Comment mainComment = cell.getCellComment();
                    if (ObjectUtil.isEmpty(mainComment)) {
                        mainComment = sheet.createDrawingPatriarch().createCellComment(
                                new XSSFClientAnchor(0, 0, 0, 0, cell.getColumnIndex(), cell.getRowIndex(), cell.getColumnIndex() + 2, cell.getRowIndex() + 3));
                        mainComment.setString(new XSSFRichTextString(message));
                        cell.setCellComment(mainComment);
                        sheet.autoSizeColumn(cell.getColumnIndex());
                    }
                }
            }
            // 临时缓冲区
            ByteArrayOutputStream out2 = new ByteArrayOutputStream();
            // 创建临时文件
            wb.write(out2);
            byte[] bookByteAry = out2.toByteArray();
            inputStream = new ByteArrayInputStream(bookByteAry);
            try {
            //只需替换这里的系统文件导出方法即可
                fileTemplate.putObject("桶名", fileName, inputStream);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    File file1 = new File(locaPath);
                    if (file1.exists() && file1.isFile()) {
                        file1.delete();
                    }
                }
            }
        }
        return fileName;
    }

导入vo对象示例

java 复制代码
@Data
@ColumnWidth(value = 30)
public class ExamExcelVo {

    @ExcelProperty(value = "考试题目", index = 0)
    private String questionStem;

    @ExcelProperty(value = "答题类型", converter = EasyExcelTopicTypeConverter.class, index = 1)
    private String questionType;

    @ExcelProperty(value = "正确答案", index = 2)
    private String answer;

    @ExcelProperty(value = "考题标签", index = 3)
    private String major;

    @ExcelProperty(value = "题要求", converter = EasyExcelDemandConverter.class, index = 4)
    private String demand;

    @ExcelProperty(value = "选项A", index = 5)
    private String optionA;

    @ExcelProperty(value = "选项B", index = 6)
    private String optionB;

    @ExcelProperty(value = "选项C", index = 7)
    private String optionC;

    @ExcelProperty(value = "选项D", index = 8)
    private String optionD;

    @ExcelProperty(value = "选项E", index = 9)
    private String optionE;


    /**
     * 判断正确选项,反射得到字段值(非必要)
     *
     * @return
     */
    public List<String> concatenateFields() {
        List<String> values = new ArrayList<>();
        try {
            if (StrUtil.isNotEmpty(answer)) {
                answer = answer.replaceAll(" ", "");
                // 使用 HashSet 来存储不重复的字母
                Set<Character> uniqueLetters = new HashSet<>();
                // 使用正则表达式匹配 A 到 Z 和 a 到 z 的字母
                Pattern pattern = Pattern.compile("[a-zA-Z]");
                Matcher matcher = pattern.matcher(answer);
                while (matcher.find()) {
                    char letter = matcher.group().charAt(0);
                    // 使用大写字母存储,以忽略大小写
                    uniqueLetters.add(Character.toUpperCase(letter));
                }
                List<String> r = new ArrayList<>();
                for (char letter : uniqueLetters) {
                    r.add(letter + "");
                }
                for (String fieldName : r) {
                    Field field = this.getClass().getDeclaredField("option" + fieldName);
                    String value = (String) field.get(this);
                    values.add(value);
                }
            }
        } catch (Exception e) {

        }
        return values;
    }
}

字段处理示例

java 复制代码
/**
 * @Author 955
 * @Date 2023-09-21 18:28
 * @Description 字段处理
 */
public class EasyExcelTopicTypeConverter implements Converter<String> {

    @Override
    public Class<String> supportJavaTypeKey() {
        return String.class;
    }

    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }

    @Override
    public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        if (StrUtil.isNotEmpty(cellData.getStringValue())) {
            //处理逻辑。。。
            //最终返回的值
            return xxx;
        }
        return null;
    }

    @Override
    public CellData convertToExcelData(String value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        if (StrUtil.isNotEmpty(value)) {
            String name = CourseBaseEnum.ExamType.getNameById(value);
            return new CellData(name);
        }
        return new CellData("");
    }
}

工具方法

java 复制代码
    /**
     * 剔除全部字段为null数据对象
     *
     * @param excelVo
     */
    private void eliminateNullList(List<ExamExcelVo> excelVo) {
        if (excelVo != null) {
            Iterator<ExamExcelVo> iterator = excelVo.iterator();
            while (iterator.hasNext()) {
                ExamExcelVo next = iterator.next();
                if (isAllFieldNull(next)) {
                    iterator.remove();
                }
            }
        }

    }

/**
     *  获取表头集合
     * @param t
     * @param objmap
     * @return
     */
    public Map<String, Integer> getFieldAnnotations(Class t, Map<String, Integer> objmap) {
        try {
            Map<String, Integer> heads = new LinkedHashMap<>();
            Field[] fields = t.getDeclaredFields();
            for (Field field : fields) {
                ExcelProperty excelpro = (ExcelProperty) field.getAnnotation(ExcelProperty.class);
                heads.put(excelpro.value()[0], excelpro.index());
//                heads.add(excelpro.value()[0]);
                objmap.put(field.getName(), excelpro.index());
            }
            return heads;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

模版导入返回带批注的错误信息方法

java 复制代码
    public R<?> importExams(List<ExamExcelVo> excelVo) {
        List<List<Object>> errorList = new ArrayList<List<Object>>();
        eliminateNullList(excelVo);
        try {
            List<ErrorTypeNewVo> errorListVo = new ArrayList<>();
            Map<String, Integer> objmap = new LinkedHashMap<>();
            Map<String, Integer> heads = getFieldAnnotations(ExamExcelVo.class, objmap);
            for (int i = 0; i < excelVo.size(); i++) {
                List<ErrorTypeNewVo> errorTypeVos = new ArrayList<ErrorTypeNewVo>();
                ExamExcelVo vo = excelVo.get(i);
                String questionStem = vo.getQuestionStem();
                ExaminationQuestion question = new ExaminationQuestion();
                if (StrUtil.isEmpty(questionStem)) {
                    errorTypeVos.add(new ErrorTypeNewVo("题目内容为空", "请重新填写", errorList.size() + 1, heads.get("考试题目")));
                } else {
                    question.setQuestionStem(questionStem);
                }
                //.......等等等等判断
                if (CollUtil.isEmpty(errorTypeVos)) {
                   //没有错误信息操作.......
                } else {
                //有错误信息操作
                    List<Object> o = new ArrayList<>();
                    Map<String, Object> map = BeanUtil.beanToMap(vo);
                    for (Map.Entry<String, Integer> entry : objmap.entrySet()) {
                        if (map.get(entry.getKey()) != null) {
                            o.add(map.get(entry.getKey()));
                        } else {
                            o.add("");
                        }
                    }
                    errorList.add(o);
                    errorListVo.addAll(errorTypeVos);
                }
            }
            if (errorListVo.size() > 0) {
                //生成错误文件
                String XSSFSheetName = "导入错误信息" + System.currentTimeMillis();
                List<String> collect = heads.keySet().stream().collect(Collectors.toList());
                //调用上方导出方法
                String BUCKET_NAME = newCreateExcelTemple_Object(XSSFSheetName, collect, errorList, errorListVo);
                return 导入有错误信息;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 导入成功信息;
    }

导入接口示例:

java 复制代码
    @ApiOperation("批量导入试题")
    @PostMapping(value = "/importExams")
    public 返回参数 importExams(@RequestExcel List<ExamExcelVo> excelVo) {
        return importExams(excelVo);
    }
相关推荐
Lizzy_Fly1 小时前
【Excel】ToRow超级查找函数
excel
mon_star°2 小时前
将答题成绩排行榜数据通过前端生成excel的方式实现导出下载功能
前端·excel
冰淇淋烤布蕾10 小时前
EasyExcel使用
java·开发语言·excel
图片转成excel表格11 小时前
Excel中怎么提取超出部分数值,比如5w是目标,超出100%和120%的值怎么用公式提取?
excel
周末zm17 小时前
golang将word、excel转换为pdf
pdf·word·excel
孟秋与你18 小时前
【excel】easy excel如何导出动态列
java·excel
Say Bay To The Bugs20 小时前
EasyExcel 使用多线程按顺序导出数据
开发语言·excel
机器懒得学习20 小时前
Python & PyQt5 实现 .his 文件批量转 Excel 工具
开发语言·python·excel
可靠百灵鸟20 小时前
Python 操作 Excel 表格从简单到高级用法
开发语言·python·excel
理想不理想v1 天前
[经典] 前端js将文件流导出为csv/excel文件
前端·javascript·excel