Poi、EasyExcel、EasyPOI导入EXCEL文档的实现方案有何区别

EasyExcel、EasyPOI导入EXCEL文档的实现方案有何区别

一.easyPoi的excel导出

Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("日程主表", "日程"), AgendaExcelVO.class, agendaExcels);

其中ExportParams第一个参数是表名,第二个参数是sheet 名字, 第三个参数是需要生成表的实体类名, 第四个是数据集合!

最主要的是实体类的注解类型:

/**
 * @author [email protected]
 * @date 2019/6/3
 */
public class AgendaExcelVO {

    /**
     * 主键
     */
    private Long id;

    /**
     * 企业主键
     */
    @Excel(name = "entId")
    private Long entId;

    /**
     * 客户经理主键
     */
    @Excel(name = "用户id")
    private Long userId;

    /**
     * 主题
     */
    @Excel(name = "主题")
    private String topic;

    /**
     * 开始时间
     */
    @Excel(name = "开始时间")
    private Date startTime;

    /**
     * 结束时间
     */
    @Excel(name = "结束时间", databaseFormat = "yyyyMMddHHmmss", format = "yyyy-MM-dd")
    private Date endTime;

    /**
     * 描述
     */
    @Excel(name = "描述")
    private String description;

需要生成的字段上面加上@Excel注解, 时间格式化用format ,这里有一个注意点,如果实体类格式是LocalDateTime,format格式化不会生效;

导出时间设置databaseFormat,如果字段是Date类型则不需要设置 数据库如果是string 类型,这个需要设置这个数据库格式,用以转换时间格式输出.

public static Workbook exportExcel(ExportParams entity, Class<?> pojoClass,
                                       Collection<?> dataSet) {
        Workbook workbook = getWorkbook(entity.getType(),dataSet.size());
        new ExcelExportService().createSheet(workbook, entity, pojoClass, dataSet);
        return workbook;
    }

    private static Workbook getWorkbook(ExcelType type, int size) {
        if (ExcelType.HSSF.equals(type)) {
            return new HSSFWorkbook();
        } else if (size < 100000) {
            return new XSSFWorkbook();
        } else {
            return new SXSSFWorkbook();
        }
    }

数据较少时,默认导出格式为xls,数据量超过100000,格式变成xlsx. 也可以根据需要自己写一个导出方法.自己放入参数

esayPoi导入excel

不校验时导入只需要一行代码

ExcelImportUtil.importExcel(new File("E://bbbb.xls"), AgendaExcelVO.class, params);

但是往往业务需要我们去校验导入的数据

1.1 excel导入校验

params.setNeedVerify(true);

实体类就可以加上注解对参数进行校验

    /**
     * 最大
     */
    @Excel(name = "Max")
    @Max(value = 15,message = "max 最大值不能超过15" ,groups = {ViliGroupOne.class})
    private int    max;
    /**
     * 最小
     */
    @Excel(name = "Min")
    @Min(value = 3, groups = {ViliGroupTwo.class})
    private int    min;
    /**
     * 非空校验
     */
    @Excel(name = "NotNull")
    @NotNull
    private String notNull;
    /**
     * 正则校验
     */
    @Excel(name = "Regex")
    @Pattern(regexp = "[\u4E00-\u9FA5]*", message = "不是中文")
    private String regex;

@Max,@Min,@NotNull,最大最小非空校验

@Pattern校验规则都是JSR 303 的,使用方式也是的

加入校验之后会返回一个ExcelImportResult 对象,比我们平时返回的list多了一些元素

public class ExcelImportResult<T> {

    /**
     * 结果集
     */
    private List<T>  list;
    /**
     * 失败数据
     */
    private List<T>  failList;

    /**
     * 是否存在校验失败
     */
    private boolean  verfiyFail;

    /**
     * 数据源
     */
    private Workbook workbook;
    /**
     * 失败的数据源
     */
    private Workbook failWorkbook;

    private Map<String,Object> map;
    }

结果包含结果集,不符合校验规则的数据集.

但是如果想同时得到全部结果集就要把实体类实现IExcelModel 接口,这样就可以返回全部结果集了.

查看源码发现excelpoi强制把workbook分成两个workbook,一个失败的一个是成功的,

 if (needMore) {
                InputStream successIs = new ByteArrayInputStream(baos.toByteArray());
                try {
                    Workbook successBook = WorkbookFactory.create(successIs);
                    importResult.setWorkbook(removeSuperfluousRows(successBook, failRow, params));
                    importResult.setFailWorkbook(removeSuperfluousRows(book, successRow, params));
                    importResult.setFailList(failCollection);
                    importResult.setVerfiyFail(verifyFail);
                } finally {
                    successIs.close();
                }
            }

所以如果想在一个workbook里面看到成功或者失败的信息只能自己手动重新生成一个workbook

二.easyExcel用法

2.1.导出

try (OutputStream out = new FileOutputStream("E://alibaba.xlsx")) {
            ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX);
            Sheet sheet = new Sheet(1, 0, PortExcelVO.class);
            writer.write(portExcelVOList, sheet);
            writer.finish();
        } catch (Exception e) {
            e.printStackTrace();
        }

实体类注解

	@ExcelProperty(value = "id", index = 0)
    private Long id;

    /**
     * 港口代码
     */
    @ExcelProperty(value = "港口代码", index = 1)
    private String code;

    /**
     * 地区
     */
    @ExcelProperty(value = "地区", index = 2)
    private Long regionId;

    /**
     * 港口类型
     */
    @ExcelProperty(value = "港口类型", index = 3)
    private Long type;

    /**
     * 名称
     */
    @ExcelProperty(value = "名称", index = 4)
    private String name;

    /**
     * 拼音全称
     */
    @ExcelProperty(value = "拼音全称", index = 5)
    private String namePinyin;

    /**
     * 拼音缩写
     */
    @ExcelProperty(value = "拼音缩写", index = 6)
    private String pinyinAbbreviate;

    /**
     * 英文名称
     */
    @ExcelProperty(value = "英文名称", index = 7)
    private String nameEn;

这里的value代表的是表头, index代表的excel列位置,从0开始

2.2导入

easyexcel 有一个抽象类

public abstract class AnalysisEventListener<T> {
    public AnalysisEventListener() {
    }

    public abstract void invoke(T var1, AnalysisContext var2);

    public abstract void doAfterAllAnalysed(AnalysisContext var1);
}

里面有两个抽象方法 分别是invoke和doAfterAllAnalysed.

每次扫描一行数据就会回调invoke() 全部扫描结束就会调用doAfterAllAnalysed()

所以我们先写一个类继承AnalysisEventListener

/**
 * @author [email protected]
 * @date 2019/6/5
 */
public class ExcelListener extends AnalysisEventListener<PortExcelVO> {

    private boolean isFail = false;

    public boolean isFail() {
        return isFail;
    }

    public ExcelListener setFail(boolean fail) {
        isFail = fail;
        return this;
    }

    private List<PortExcelVO> portExcelVOList = new ArrayList<>();

    public List<PortExcelVO> getPortExcelVOList() {
        return portExcelVOList;
    }

    public ExcelListener setPortExcelVOList(List<PortExcelVO> portExcelVOList) {
        this.portExcelVOList = portExcelVOList;
        return this;
    }

    @Override
    public void invoke(PortExcelVO portExcelVO, AnalysisContext analysisContext) {
        System.out.println("portExcelVO::::::::::::::" + portExcelVO);
        Integer currentRowNum = analysisContext.getCurrentRowNum();
        System.out.println("当前行:::" + currentRowNum);

        // 1.校验参数,不符合条件插入新数据 isFail设置为true
        StringBuilder failReason = new StringBuilder();
		// 自定义逻辑
        if (portExcelVO.getType() == 1) {
            failReason.append("Type cannot equals one;");
            setFail(true);
        }
        if (portExcelVO.getName().equals("主港")) {
            failReason.append("名字是主港");
            setFail(true);
        }

        // 放入集合
        if (isFail) {
            portExcelVO.setFailReason(failReason.toString());
        }

    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        // 如果有错误数据就导出excel表格
        if (isFail) {
            // 导出表格
            exportExcel(portExcelVOList);
        } else {
            // 导入数据库
            importDataBase(portExcelVOList);
        }

        // 销毁不用资源,设置isFail为false
        portExcelVOList.clear();
        setFail(false);
    }

    private void exportExcel(List<PortExcelVO> portExcelVOList) {
        System.out.println("++++++++++++++++++++++导出数据");
        ExportExcelUtil.exportEasyExcel(portExcelVOList);
    }

    private void importDataBase(List<PortExcelVO> portExcelVOList) {
        // TODO
        System.out.println("++++++++++++++++++++++++++++++++++++++导入数据库");
    }
}

然后我们实例化这个对象, 就可以按照我们的逻辑导入数据了

导入代码如下

try (InputStream inputStream = new FileInputStream("E://alibaba.xlsx")) {
            // 解析每行结果在listener中处理
            ExcelListener listener = new ExcelListener();
            ExcelReader excelReader = new ExcelReader(inputStream, ExcelTypeEnum.XLSX, null, listener);
            excelReader.read();
        } catch (Exception e) {
            e.printStackTrace();
        }

三.两者性能对比

Poi、EasyExcel、EasyPOI导入EXCEL文档的实现方案有何区别_第1张图片

easyPoi导出2000条数据好事324毫秒

Poi、EasyExcel、EasyPOI导入EXCEL文档的实现方案有何区别_第2张图片

easyExcel耗时608毫秒

Poi、EasyExcel、EasyPOI导入EXCEL文档的实现方案有何区别_第3张图片

Poi、EasyExcel、EasyPOI导入EXCEL文档的实现方案有何区别_第4张图片

对一个接口同时点击多次内存占用对比可以看出easyExcel内存明显较小

总结

从easypoi和easyExcel 的 源码

这两者都是引用Apache的poi 但是区别就是两者的解析不同

easypoi的解析方式是dom解析,把结果一次都读入内存操作,这样的操作平时是不会有问题的,但是并发量上来的时候就会出现OOM

而阿里的easyExcel 运用的SAX的解析方式,明显降低了内存,但是速率下降

你可能感兴趣的:(java)