springboot集成easyExcel实现文件导入导出

EasyExcel简介

EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。

EasyExcel使用

导入依赖

		<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.1</version>
        </dependency>

excel映射实体的父类

import java.util.HashMap;
import java.util.List;

public abstract class ExcelModel {

    /**
     * 验证参数有效性
     * @return 返回验证结果
     * @param args 参数
     */
    public abstract boolean validation(String... args);

    /**
     * excel表头注释
     * @return 返回注释信息
     */
    public abstract HashMap<Integer, String> getAnnotationsMap();

    /**
     * excel 下拉选内容
     * @return 返回下拉选信息
     */
    public abstract HashMap<Integer, String[]> getDropDownMap();

    /**
     * 重点行
     * @return 返回重点行信息
     */
    public abstract List<Integer> getEmphasizeColumns();
}

单元格格式处理类

import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.util.StyleUtil;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.*;

import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;

@Slf4j  
public class EasyExcelTitleHandler implements CellWriteHandler {
    /**
   错误信息处理时正则表达式的格式
    */
   private final String EXCEL_ERROR_REG = "^(.*)(\\(错误:)(.*)(\\))$";
   /**
   操作列
    */
   private final List<Integer> columnIndex;
   /**
   颜色
    */
   private final Short colorIndex;
   /**
    批注<列的下标,批注内容>
    */
   private final HashMap<Integer,String> annotationsMap;
   /**
    下拉框值
    */
   private final HashMap<Integer,String[]> dropDownMap;

   public EasyExcelTitleHandler(List<Integer> columnIndex, Short colorIndex, HashMap<Integer, String> annotationsMap, HashMap<Integer, String[]> dropDownMap) {
       this.columnIndex = columnIndex;
       this.colorIndex = colorIndex;
       this.annotationsMap = annotationsMap;
       this.dropDownMap = dropDownMap;
   }

   @Override
   public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
   }

   @Override
   public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
   }

   @Override
   public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
       if(isHead){
           // 设置列宽
           Sheet sheet = writeSheetHolder.getSheet();
           writeSheetHolder.getSheet().getRow(0).setHeight((short)(1.8*256));
           Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
           Drawing<?> drawing = sheet.createDrawingPatriarch();

           // 设置标题字体样式
           WriteCellStyle headWriteCellStyle = new WriteCellStyle();
           WriteFont headWriteFont = new WriteFont();
           headWriteFont.setFontName("宋体");
           headWriteFont.setFontHeightInPoints((short)14);
           headWriteFont.setBold(true);
           if (CollectionUtils.isNotEmpty(columnIndex) &&
                   colorIndex != null &&
                   columnIndex.contains(cell.getColumnIndex())) {
               // 设置字体颜色
               headWriteFont.setColor(colorIndex);
           }
           headWriteCellStyle.setWriteFont(headWriteFont);
           headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
           CellStyle cellStyle = StyleUtil.buildHeadCellStyle(workbook, headWriteCellStyle);
           cell.setCellStyle(cellStyle);

           if (null != annotationsMap && annotationsMap.containsKey(cell.getColumnIndex())) {
               // 批注内容
               String context = annotationsMap.get(cell.getColumnIndex());
               // 创建绘图对象
               Comment comment=drawing.createCellComment(new XSSFClientAnchor(0, 0, 0,0, (short) cell.getColumnIndex(), cell.getRowIndex(), (short) cell.getColumnIndex()+2,  (short) cell.getRowIndex()+2));
               comment.setString(new XSSFRichTextString(context));
               cell.setCellComment(comment);
           }

           if(null != dropDownMap && !dropDownMap.isEmpty() &&
                   dropDownMap.containsKey(cell.getColumnIndex())){
               String[] data = dropDownMap.get(cell.getColumnIndex());
               DataValidationHelper dvHelper = sheet.getDataValidationHelper();
               DataValidationConstraint dvConstraint = dvHelper.createExplicitListConstraint(data);
               CellRangeAddressList addressList = new CellRangeAddressList(1, 65535, cell.getColumnIndex(), cell.getColumnIndex());
               DataValidation validation = dvHelper.createValidation(dvConstraint, addressList);
               validation.createPromptBox("提示", "只能选择下拉框里的值");
               validation.setSuppressDropDownArrow(true);
               validation.setShowPromptBox(true);
               validation.setShowErrorBox(true);
               validation.setEmptyCellAllowed(false);
               sheet.addValidationData(validation);
               dropDownMap.remove(cell.getColumnIndex());
           }
       }
       // cell 匹配到错误信息规则,cell 字体设置为红色
       Pattern pat = Pattern.compile(EXCEL_ERROR_REG);
       if(pat.matcher(cell.getStringCellValue()).matches()) {
           Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
           CellStyle cellStyle1 = workbook.createCellStyle();
           Font cellFont = workbook.createFont();
           assert colorIndex != null;
           cellFont.setColor(colorIndex);
           cellStyle1.setFont(cellFont);
           cell.setCellStyle(cellStyle1);
       }
   }
}

导入数据处理类

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.xxx.ExcelModel;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class EasyExcelListener<T extends ExcelModel> extends AnalysisEventListener<T> {

    /**
     *  用于保存正常的数据
     */
    private List<T> data = Collections.synchronizedList(new ArrayList<>());

    /**
     *  用于保存异常数据
     */
    private List<T> error = Collections.synchronizedList(new ArrayList<>());

    private String[] arg;

    @Override
    public void invoke(T o, AnalysisContext analysisContext) {
        if(o.validation(arg)) {
            data.add(o);
        }else {
            error.add(o);
            System.out.println("error--" + o);
        }
    }

    /**
     *  excel 读取完之后的操作
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
//        analysisContext.
    }

    public List<T> getData() {
        return data;
    }

    public void setData(List<T> data) {
        this.data = data;
    }

    public List<T> getError() {
        return error;
    }

    public void setError(List<T> error) {
        this.error = error;
    }

    public void setArg(String[] arg) {
        this.arg = arg;
    }
}

excelUtil工具类

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.xxx.ExcelModel;
import org.apache.poi.ss.usermodel.IndexedColors;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;

public class EasyExcelUtil<T extends ExcelModel> {
    /**
     * 导出excel
     * @param outputStream 输出流
     * @param dataList     导出的数据
     * @param classT        模板类
     * @param sheetName     sheetName
     * @param cellWriteHandlers    样式处理类
     */
    public void writeExcelWithModel(OutputStream outputStream, List<T> dataList, Class<? extends ExcelModel> classT, String sheetName, CellWriteHandler... cellWriteHandlers) {

        // 头的策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 单元格策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        // 初始化表格样式
        HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);


        ExcelWriterSheetBuilder excelWriterSheetBuilder = EasyExcel.write(outputStream, classT).sheet(sheetName).registerWriteHandler(horizontalCellStyleStrategy);
        if (null != cellWriteHandlers && cellWriteHandlers.length > 0) {
            for (CellWriteHandler cellWriteHandler : cellWriteHandlers) {
                excelWriterSheetBuilder.registerWriteHandler(cellWriteHandler);
            }
        }
        // 开始导出
        excelWriterSheetBuilder.doWrite(dataList);
    }

    /**
     * 使用 模型 来读取Excel
     * @param fileInputStream Excel的输入流
     * @param clazz 模型的类
     * @param arg 验证用的参数,可以没有
     * @return 返回 模型 的列表(为object列表,需强转)
     */
    public EasyExcelListener<T> readExcelWithModel(InputStream fileInputStream, Class<?> clazz, String... arg) {
        EasyExcelListener<T> listener = new EasyExcelListener<>();
        listener.setArg(arg);
        ExcelReader excelReader = EasyExcel.read(fileInputStream, clazz, listener).build();
        ReadSheet readSheet = EasyExcel.readSheet(0).build();
        excelReader.read(readSheet);
        excelReader.finish();
        return listener;
    }

    public void createExcel(OutputStream os, List<T> data, T t, String sheetName) {

        // 指定标红色的列
        List<Integer> columns = t.getEmphasizeColumns();
        // 指定批注
        HashMap<Integer, String> annotationsMap = t.getAnnotationsMap();
        // 指定下拉选
        HashMap<Integer, String[]> dropDownMap = t.getDropDownMap();

        EasyExcelTitleHandler easyExcelTitleHandler = new EasyExcelTitleHandler(columns, IndexedColors.RED.index, annotationsMap, dropDownMap);

        writeExcelWithModel(os, data, t.getClass(), sheetName, easyExcelTitleHandler);

    }

}

使用示例

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.xxx.ExcelModel;
import com.xxx.EasyExcelListener;
import com.xxx.EasyExcelUtil;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;

public class ExcelWriteTest {

    String[] clazzList = {"一年级1班", "一年级2班", "一年级3班"};

    /**
     * 测试excel导出
     */
    @Test
    public void excelExport() throws IOException {
        // 输出流
        OutputStream outputStream = new FileOutputStream("D:\\test.xlsx");
        // 准备要导出的数据
        List<TestModel> data = new ArrayList<>();
        //获取excelUtil实例
        EasyExcelUtil<TestModel> excelUtil = new EasyExcelUtil<>();
        //设置导出时下拉选的内容
        TestModel testModel = new TestModel();
        //动态指定下拉选内容
        testModel.setClazzList(clazzList);
        //导出excel
        try {
            excelUtil.createExcel(outputStream, data, testModel, "用户导入模板");
        }catch (Exception e) {
            //导出失败的处理
            e.printStackTrace();
        }

   }

    /**
     * 测试excel导入
     */
    @Test
    public void excelImport() throws FileNotFoundException {
        InputStream inputStream;
        inputStream = new FileInputStream("D:\\test.xlsx");
        EasyExcelUtil<TestModel> easyExcelUtil = new EasyExcelUtil<>();

        //导入excel,clazzList为动态指定的校验列表
        EasyExcelListener<TestModel> listener =
                easyExcelUtil.readExcelWithModel(inputStream, TestModel.class, clazzList);
        System.out.println("格式正确的数据-data:----");
        listener.getData().forEach(System.out::println);
        System.out.println("格式异常的数据-error:----");
        listener.getError().forEach(System.out::println);
    }

    @EqualsAndHashCode(callSuper = true)
    @Data
    public static class TestModel extends ExcelModel {

        /**
         * 动态指定下拉选的内容,或校验时的内容
         */
        @ExcelIgnore
        String[] clazzList;

        /**
         * excel导出不需要的属性
         */
        @ExcelIgnore
        private String id;

        @ExcelProperty(value = {"班级", "班级"}, index = 0)
        private String clazz;

        /**
         * value对应excel表头,index对应excel列数,第一列为 0
         */
        @ExcelProperty(value = {"姓名", "姓名"}, index = 1)
        private String name;

        @ExcelProperty(value = {"年龄", "年龄"}, index = 2)
        private String age;

        /**
         * 合并单元格的表头
         */
        @ExcelProperty(value = {"成绩", "数学"}, index = 3)
        //excel映射实体使用string字段,避免excel数据类型和java数据类型转换问题
        private String scoreMath;

        @ExcelProperty(value = {"成绩", "英语"}, index = 4)
        private String scoreEnglish;

        @Override
        public boolean validation(String... args) {

            //args为校验数据需要的变量,从listener中传入,如无需要可没有

            boolean result = true;
            //校验导入的数据
            if(StringUtils.isBlank(this.getClazz()) || !Arrays.asList(args).contains(this.getClazz())) {
                result = false;
                //错误信息的格式需要对应 错误信息处理时正则表达式的格式
                this.setClazz(this.getClazz() + "(错误:请使用下拉选的值或使用最新模板)");
            }
            return result;
        }

        @Override
        public HashMap<Integer, String> getAnnotationsMap() {
            // 指定批注
            HashMap<Integer, String> annotationsMap = new HashMap<>();
            annotationsMap.put(0, "必填,只能使用下拉选中的值");
            return annotationsMap;
        }

        @Override
        public HashMap<Integer, String[]> getDropDownMap() {
            HashMap<Integer, String[]> dropDownMap = new HashMap<>();
            // 指定下拉框
            String[] classList = this.clazzList;
            dropDownMap.put(0,classList);
            return dropDownMap;
        }

        @Override
        public List<Integer> getEmphasizeColumns() {
            //标记比填列 —— excel并未做限制
            return Arrays.asList(0, 1, 3, 4);
        }
    }
}

动态设置表头和单元格移步下一篇文章:easyExcel实现动态表头设置以及单元格样式设置

你可能感兴趣的:(spring,boot,excel,后端)