前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >更简单的Excel导入方式,easypoi了解一下

更简单的Excel导入方式,easypoi了解一下

作者头像
码农飞哥
发布2021-08-18 10:42:01
1.7K0
发布2021-08-18 10:42:01
举报
文章被收录于专栏:好好学习

概况

今天做Excel导出时,发现了一款非常好用的POI框架EasyPoi,其 使用起来简洁明了。现在我们就来介绍下EasyPoi,首先感谢EasyPoi 的开发者 Lemur开源

easypoi 简介

easypoi 是为了让开发者快速的实现excel,word,pdf的导入导出,基于Apache poi基础上的一个工具。

使用

  1. SSM 项目,引入依赖 如果spring的版本是4.x的话引入的easypoi的版本是`3.0.1`,如果spring是5.x的话引入easypoi的版本是`4.0.0`
代码语言:javascript
复制
<dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-base</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-web</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-annotation</artifactId>
            <version>4.0.0</version>
        </dependency>
  1. Spring Boot 项目(2.x以上的版本),引入依赖
代码语言:javascript
复制
    <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-spring-boot-starter</artifactId>
            <version>4.0.0</version>
        </dependency>

需要注意的是由于easypoi的依赖内部依赖原生的poi,所以,引入了easypoi的依赖之后,需要把原生的poi删除

特性

  1. 基于注解的导入导出,修改注解就可以修改Excel
  2. 支持常用的样式自定义
  3. 基于map可以灵活定义的表头字段
  4. 支持一对多的导出,导入
  5. 支持模板的导出,一些常见的标签,自定义标签
  6. 支持HTML/Excel转换
  7. 支持word的导出,支持图片,Excel

注解方式

  1. 常用注解
  • @Excel 作用到filed 上面,是对Excel一列的一个描述,这个注解是必须要的注解,其部分属性如下:

在这里插入图片描述 其中 name_id 的说明:例如:

代码语言:javascript
复制
@ExcelTarget("teacherEntity")
public class TeacherEntity implements java.io.Serializable {
    /** name */
    @Excel(name = "主讲老师_teacherEntity,代课老师_absent", orderNum = "1", mergeVertical = true,needMerge=true,isImportField = "true_major,true_absent")
    private String name;

这里的@ExcelTarget 表示使用teacherEntity这个对象是可以针对不同字段做不同处理 同样的ExcelEntity 和ExcelCollection 都支持这种方式 当导出这对象时,name这一列对应的是主讲老师,而不是代课老师还有很多字段都支持这种做法

  • @ExcelCollection 表示一个集合,主要针对一对多的导出 比如一个老师对应多个科目,科目就可以用集合表示,属性如下:

在这里插入图片描述

  • @ExcelEntity表示一个继续深入导出的实体

在这里插入图片描述

  • @ExcelIgnore 和名字一样表示这个字段被忽略跳过这个导出
  • @ExcelTarget 作用于最外层的对象,描述这个对象的id,以便支持一个对象 可以针对不同导出做出不同处理

在这里插入图片描述

注解方式导出Excel参考demo

代码语言:javascript
复制
    /**
     * 基本导出测试
     * 
     * @throws Exception
     */
    @Test
    public void testExportExcel() throws Exception {
        Date start = new Date();
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("2412312", "测试", "测试"),
                CourseEntity.class, list);
        System.out.println(new Date().getTime() - start.getTime());
        File savefile = new File("D:/excel/");
        if (!savefile.exists()) {
            savefile.mkdirs();
        }
        FileOutputStream fos = new FileOutputStream("D:/excel/基本导出测试.xls");
        workbook.write(fos);
        fos.close();
    }

CourseEntity 类。

代码语言:javascript
复制
@ExcelTarget("courseEntity")
public class CourseEntity implements java.io.Serializable {
    /** 主键 */
    private String        id;
    /** 课程名称 */
    @Excel(name = "课程名称", orderNum = "1", width = 25,needMerge = true)
    private String        name;
    /** 老师主键 */
    //@ExcelEntity(id = "major")
    private TeacherEntity chineseTeacher;
    /** 老师主键 */
    @ExcelEntity(id = "absent")
    private TeacherEntity mathTeacher;

    @ExcelCollection(name = "学生", orderNum = "4")
    private List<StudentEntity> students;

StudentEntity 类。

代码语言:javascript
复制
public class StudentEntity implements java.io.Serializable {
    /**
     * id
     */
    private String        id;
    /**
     * 学生姓名
     */
    @Excel(name = "学生姓名", height = 20, width = 30, isImportField = "true_st")
    private String        name;
    /**
     * 学生性别
     */
    @Excel(name = "学生性别", replace = { "男_1", "女_2" }, suffix = "生", isImportField = "true_st")
    private int           sex;

    @Excel(name = "出生日期", exportFormat = "yyyyMMddHHmmss", format = "yyyy-MM-dd", isImportField = "true_st", width = 20)
    private Date          birthday;

    @Excel(name = "进校日期", exportFormat = "yyyyMMddHHmmss", format = "yyyy-MM-dd")
    private Date registrationDate;

关于日期格式化的说明

  1. 如果是导出的实体类(就是说这个实体类是对应导出的Excel的),那么用@Excel注解的exportFormat属性来格式化日期。如下所示:
代码语言:javascript
复制
 @Excel(name = "出生日期", exportFormat = "yyyy-MM-dd HH:mm:ss", width = 20)
  1. 如果是导入的实体类(就是说这个实体类是对应导入的Excel的),那么用@Excel注解的importFormat属性来格式化日期。如下所示:
代码语言:javascript
复制
   @Excel(name = "添加时间",importFormat =  "yyyy-MM-dd HH:mm:ss",orderNum = "14")
    private Date createTime;
  1. @Excel注解的databaseFormat 属性是用于数据库的格式不是日期类型datetime类型时用。 注解方式导入Excel

1. 介绍

基于注解的导入导出,配置配置上是一样的,只是方式反过来而已,比如类型的替换 导出的时候是1替换成男,2替换成女,导入的时候则反过来,男变成1 ,女变成2,时间也是类似导出的时候date被格式化成 2017-8-25 ,导入的时候2017-8-25被格式成date类型

代码语言:javascript
复制
 @Test
    public void test2() {
        ImportParams params = new ImportParams();
        long start = new Date().getTime();
        List<MsgClient> list = ExcelImportUtil.importExcel(
           new File(PoiPublicUtil.getWebRootPath("import/ExcelExportMsgClient.xlsx")),
            MsgClient.class, params);
        System.out.println(new Date().getTime() - start);
        System.out.println(list.size());
        System.out.println(ReflectionToStringBuilder.toString(list.get(0)));
    }

在这里插入图片描述

ImportParams

ImportParams这个类是用于设置导入参数的。我们来看下这个类

代码语言:javascript
复制
public class ImportParams extends ExcelBaseParams {

    public static final String SAVE_URL = "/excel/upload/excelUpload";

    /**
     * 表格标题行数,默认0
     */
    private int                 titleRows        = 0;
    /**
     * 表头行数,默认1
     */
    private int                 headRows         = 1;
    /**
     * 字段真正值和列标题之间的距离 默认0
     */
    private int                 startRows        = 0;

    /**
     * 主键设置,如何这个cell没有值,就跳过 或者认为这个是list的下面的值
     * 大家不理解,去掉这个
     */

    private Integer             keyIndex         = null;
    /**
     * 开始读取的sheet位置,默认为0
     */
    private int                 startSheetIndex  = 0;
    /**
     * 上传表格需要读取的sheet 数量,默认为1
     */
    private int                 sheetNum         = 1;
    /**
     * 是否需要保存上传的Excel,默认为false
     */
    private boolean             needSave         = false;
    /**
     * 校验组
     */
    private Class[]             verifyGroup = null;
    /**
     * 是否需要校验上传的Excel,默认为false
     */
    private boolean             needVerify = false;
    /**
     * 校验处理接口
     */
    private IExcelVerifyHandler verifyHandler;
    /**
     * 保存上传的Excel目录,默认是 如 TestEntity这个类保存路径就是
     * upload/excelUpload/Test/yyyyMMddHHmss_***** 保存名称上传时间_五位随机数
     */
    private String              saveUrl          = SAVE_URL;
    /**
     * 最后的无效行数
     */
    private int                 lastOfInvalidRow = 0;
    /**
     * 手动控制读取的行数
     */
    private int                 readRows = 0;
    /**
     * 导入时校验数据模板,是不是正确的Excel
     */
    private String[]            importFields;
    /**
     * 导入时校验excel的标题列顺序。依赖于importFields的配置顺序
    */
    private boolean             needCheckOrder = false;
    /**
     * Key-Value 读取标记,以这个为Key,后面一个Cell 为Value,多个改为ArrayList
     */
    private String              keyMark = ":";
    /**
     * 按照Key-Value 规则读取全局扫描Excel,但是跳过List读取范围提升性能
     * 仅仅支持titleRows + headRows + startRows 以及 lastOfInvalidRow
     */
    private boolean             readSingleCell = false;

需要说明的是titleRows默认是0,如果我们没有标题行的话就不要去设置这个值,这里的标题不是表头 情形一:

在这里插入图片描述 如上,这个Excel没有标题,其导入的话就不需要设置titleRows的值,headRows的值也可以直接用默认的。 情形二:

在这里插入图片描述 如上,这个Excel是有标题的,其导入时我们就需要设置titleRows和headRows的值,如下标题行设置为1,表头行设置为2。

代码语言:javascript
复制
      ImportParams params = new ImportParams();
        params.setTitleRows(1);
        params.setHeadRows(2);

2. Excel导入校验

对象 EasyPoi的校验使用也很简单,对象上加上通用的校验规则或者这定义的这个看你用的哪个实现 然后params.setNeedVerfiy(true);配置下需要校验就可以了 看下具体的代码

代码语言:javascript
复制
/**
     * Email校验
     */
    @Excel(name = "Email", width = 25)
    private String email;
    /**
     * 最大
     */
    @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;

使用方式

代码语言:javascript
复制
@Test
    public void basetest() {
        try {
            ImportParams params = new ImportParams();
            params.setNeedVerfiy(true);
            params.setVerfiyGroup(new Class[]{ViliGroupOne.class});
            ExcelImportResult<ExcelVerifyEntity> result = ExcelImportUtil.importExcelMore(
                new File(PoiPublicUtil.getWebRootPath("import/verfiy.xlsx")),
                ExcelVerifyEntity.class, params);
            FileOutputStream fos = new FileOutputStream("D:/excel/ExcelVerifyTest.basetest.xlsx");
            result.getWorkbook().write(fos);
            fos.close();
            for (int i = 0; i < result.getList().size(); i++) {
                System.out.println(ReflectionToStringBuilder.toString(result.getList().get(i)));
            }
            Assert.assertTrue(result.getList().size() == 1);
            Assert.assertTrue(result.isVerfiyFail());
        } catch (Exception e) {
            LOGGER.error(e.getMessage(),e);
  1. 导入结果ExcelImportResult 导入之后返回一个ExcelImportResult 对象,比我们平时返回的list多了一些元素
代码语言:javascript
复制
/**
     * 结果集
     */
    private List<T>  list;

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

    /**
     * 数据源
     */
    private Workbook workbook;

一个是集合,是一个是是否有校验失败的数据,一个原本的文档,但是在文档后面追加了错误信息

注意,这里的list,有两种返回

一种是只返回正确的数据 一种是返回全部的数据,但是要求这个对象必须实现IExcelModel接口,如下 IExcelModel

代码语言:javascript
复制
public class ExcelVerifyEntityOfMode extends ExcelVerifyEntity implements IExcelModel {

    private String errorMsg;

    @Override
    public String getErrorMsg() {
        return errorMsg;
    }

    @Override
    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }

}

IExcelDataModel 获取错误数据的行号

代码语言:javascript
复制
public interface IExcelDataModel {

    /**
     * 获取行号
     * @return
     */
    public int getRowNum();

    /**
     *  设置行号
     * @param rowNum
     */
    public void setRowNum(int rowNum);

}

需要对象实现这个接口

每行的错误数据也会填到这个错误信息中,方便用户后面自定义处理 看下代码

代码语言:javascript
复制
 @Test
    public void baseModetest() {
        try {
            ImportParams params = new ImportParams();
            params.setNeedVerfiy(true);
            ExcelImportResult<ExcelVerifyEntityOfMode> result = ExcelImportUtil.importExcelMore(
                    new FileInputStream(new File(PoiPublicUtil.getWebRootPath("import/verfiy.xlsx"))),
                ExcelVerifyEntityOfMode.class, params);
            FileOutputStream fos = new FileOutputStream("D:/excel/baseModetest.xlsx");
            result.getWorkbook().write(fos);
            fos.close();
            for (int i = 0; i < result.getList().size(); i++) {
                System.out.println(ReflectionToStringBuilder.toString(result.getList().get(i)));
            }
            Assert.assertTrue(result.getList().size() == 4);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(),e);
        }
    }

定制化修改

有时候,我们需要定制化一些信息,比如,导出Excel时,我们需要统一Excel的字体的大小,字型等。我们可以通过实现IExcelExportStyler接口或者继承ExcelExportStylerDefaultImpl类来实现。如下所示:我们定义了一个ExcelStyleUtil工具类继承了ExcelExportStylerDefaultImpl(样式的默认实现类),并且将列头,标题,单元格的字体都设置为了宋体。

代码语言:javascript
复制
public class ExcelStyleUtil extends ExcelExportStylerDefaultImpl {

    public ExcelStyleUtil(Workbook workbook) {
        super(workbook);
    }
    /**
     * 标题样式
     */    
    @Override
    public CellStyle getTitleStyle(short color) {
        CellStyle cellStyle = super.getTitleStyle(color);
        cellStyle.setFont(getFont(workbook, 11, false));
        return cellStyle;
    }
     /**
     * 单元格的样式
     */
    @Override
    public CellStyle stringSeptailStyle(Workbook workbook, boolean isWarp) {
        CellStyle cellStyle = super.stringSeptailStyle(workbook, isWarp);
        cellStyle.setFont(getFont(workbook, 11, false));
        return cellStyle;
    }
     /**
     * 列表头样式
     */
    @Override
    public CellStyle getHeaderStyle(short color) {
        CellStyle cellStyle =  super.getHeaderStyle(color);
        cellStyle.setFont(getFont(workbook, 11, false));
        return cellStyle;
    }
     /**
     * 单元格的样式
     */
    @Override
    public CellStyle stringNoneStyle(Workbook workbook, boolean isWarp) {
        CellStyle cellStyle = super.stringNoneStyle(workbook, isWarp);
        cellStyle.setFont(getFont(workbook, 11, false));
        return cellStyle;
    }

    /**
     * 字体样式
     *
     * @param size   字体大小
     * @param isBold 是否加粗
     * @return
     */
    private Font getFont(Workbook workbook, int size, boolean isBold) {
        Font font = workbook.createFont();
        //字体样式
        font.setFontName("宋体");
        //是否加粗
        font.setBold(isBold);
        //字体大小
        font.setFontHeightInPoints((short) size);
        return font;
    }

}

然后就是对ExcelExportUtil类进行包装,如下所示:

代码语言:javascript
复制
public class OfficeExportUtil {

    /**
     *
     */
    private static final Integer EXPORT_EXCEL_MAX_NUM = 20000;

    /**
     * 获取导出的Workbook对象
     *
     * @param sheetName 页签名
     * @param clazz     类对象
     * @param list      导出的数据集合
     * @return
     * @author xiagwei
     * @date 2020/2/10 4:45 PM
     */
    public static Workbook getWorkbook(String sheetName, Class clazz, List<?> list) {
        //判断数据是否为空
        if (CollectionUtils.isEmpty(list)) {
            log.info("***********导出数据行数为空!");
            list = new ArrayList<>();
        }
        if (list.size() > EXPORT_EXCEL_MAX_NUM) {
            log.info("***********导出数据行数超过:" + EXPORT_EXCEL_MAX_NUM + "条,无法导出、请添加导出条件!");
            list = new ArrayList<>();
        }
        log.info("***********"+sheetName+"的导出数据行数为"+list.size()+"");
        //获取导出参数
        ExportParams exportParams = new ExportParams();
        //设置导出样式
        exportParams.setStyle(ExcelStyleUtil.class);
        //设置sheetName
        exportParams.setSheetName(sheetName);
        //输出workbook流
        return ExcelExportUtil.exportExcel(exportParams, clazz, list);
    }

使用的话,我们只需要获取需要导出的数据,然后调用OfficeExportUtil的getWorkbook方法。

总结

本文主要介绍了easypoi的使用和相关属性,easypoi使用起来还是蛮简单的。但是有个缺点是导入导出大批量数据时性能没那么好。

参考代码

https://gitee.com/lemur/easypoi-test

参考

EasyPoi教程

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农飞哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概况
  • easypoi 简介
  • 使用
  • 特性
  • 注解方式
    • 注解方式导出Excel参考demo
      • 关于日期格式化的说明
        • 1. 介绍
          • ImportParams
            • 2. Excel导入校验
            • 定制化修改
            • 总结
            • 参考代码
            • 参考
            相关产品与服务
            数据库
            云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档