专栏首页码农UP2U省内存的 Excel 导入导出库还是得了解下它...

省内存的 Excel 导入导出库还是得了解下它...

早期文章

在各种系统当中,Excel 的导入与导出功能是最为基础的功能,无论是商品信息的导入、工资的导出,实在太常见了。在 Java 项目中,可以完成 Excel 导入导出的类库有很多种,这里来介绍一下 阿里巴巴 开源的 EasyExcel ,该库的特点如下:

Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便 https://github.com/alibaba/easyexcel

从上面可以看出,EasyExcel 占用内存方面比 Apache poi、jxl 要小很多,在使用上也较为简单。本篇文章通过简单的实例,来快速的上手 EasyExcel 的使用。下面给一个 EasyExcel 内存使用图,该图来自 EasyExcel 的官网,如下图。

一、引入依赖

使用 EasyExcel 的第一步就是引入依赖。我们创建一个 SpringBoot 的项目,然后在 pom.xml 文件中引入 EasyExcel 的依赖,依赖如下:

<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.1.1</version>
</dependency>

引入依赖后就可以按照套路来使用 EasyExcel 了。

二、创建 Excel 对应的实体类

在各种系统中,导入导出的数据一般都是通过 Excel 来完成的,在实现具体的导入导出功能前,首先是确定要导入导出哪些数据,也就是确定 Excel 文件的数据格式。在实际的导入导出功能中,数据一般是通过查询数据库获取的,这部分功能由程序员自己来完成实现,而 Excel 的导入导出实际是由类库帮助我们完成。

同样的,对于导入导出功能,我们需要将我们指定的数据格式告诉导入导出 Excel 的类库。那么,首先需要建立一个和 Excel 表格对应的数据格式实体类。这里来写一个简单的学生类。

@Data
public class Student {

    /**
     * 姓名
     */
    @ExcelProperty("姓名")
    private String name;

    /**
     * 性别
     */
    @ExcelProperty("性别")
    private Integer gender;

    /**
     * 电话
     */
    @ExcelProperty("电话")
    private String phone;
}

在上面定义的实体类中通过 @ExcelProperty 注解来指定属性对应的 Excel 中列的名称。EasyExcel 还提供了类似设置 列宽、列高 等注解,比如:

@ContentRowHeight(10)
@HeadRowHeight(20)
@ColumnWidth(25)

具体各种注解的用法及细节可以参考官网文档进行学习和了解。

三、导出数据为 Excel

有了与 Excel 对应的实体类后,我们先来完成一个导出 Excel 的功能。这里,我们创建一个测试用例,来模拟将数据导出为 Excel 的过程。

@Test
void Export()
{
    List<Student> stuList = new ArrayList<>();

    for (int i = 0; i < 10; i ++) {
        Student student = new Student();

        student.setName("stu" + i);
        student.setGender(i % 2);
        student.setPhone("1391234432" + i);

        stuList.add(student);
    }

    String exportName = "./export.xlsx";

    EasyExcel.write(exportName, Student.class)
            .sheet("学生信息")
            .doWrite(stuList);
}

在上面的代码中,通过循环创建了 10 条数据保存在了 List 集合当中,然后将 List 集合导出到 Excel 中。导出的 Excel 结果如下图所示:

在实际的项目中,导出的数据通常来自于数据库,这里为了 Demo 的简洁,通过 List 集合来进行模拟,但是实质是相同的。

四、读取 Excel 中的数据

在读取 Excel 的内容时,EasyExcel 类库为我们提供了同步读取,或者创建监听器的方式进行读取。同步读取的方式较为简单,但是官方并不建议使用,因为会将大量的数据放入内存。

官网使用监听器的方式进行 Excel 文件的读取,首先需要创建一个继承 AnalysisEventListener 类的监听器类,其中会实现几个方法,比如 invoke 和 doAfterAllAnalysed 。invoke 方法是每一条数据解析都会调用该方法,在所有的数据都解析完成了会调用 doAfterAllAnalysed 方法

我们的 Demo 只完成简单的输出,并不实际写入数据库,因此比较简单。具体代码如下:

public class ExcelListener extends AnalysisEventListener<Student> {
    @Override
    public void invoke(Student student, AnalysisContext analysisContext) {
        System.out.println(student);
    }

    @Override
    public void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context) {
        System.out.println("表头信息" + headMap);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }
}

编写一个测试用例,使用 EasyExcel 来读取 Excel 的内容,代码如下:

@Test
void Import()
{
    String importName = "./export.xlsx";

    EasyExcel.read(importName, Student.class, new ExcelListener())
            .sheet().doRead();
}

执行上面的测试用例,输出结果如下:

表头信息{0=姓名, 1=性别, 2=电话}
Student(name=stu0, gender=0, phone=13912344320)
Student(name=stu1, gender=1, phone=13912344321)
Student(name=stu2, gender=0, phone=13912344322)
Student(name=stu3, gender=1, phone=13912344323)
Student(name=stu4, gender=0, phone=13912344324)
Student(name=stu5, gender=1, phone=13912344325)
Student(name=stu6, gender=0, phone=13912344326)
Student(name=stu7, gender=1, phone=13912344327)
Student(name=stu8, gender=0, phone=13912344328)
Student(name=stu9, gender=1, phone=13912344329)

上面的输出结果就是读入我们前面导出的数据。

五、通过 Web 导出数据为 Excel 文件

在项目中,通常是通过一个页面来完成数据导入,我们可以写一个接口来供前端进行使用。通过 Web 页面导入 Excel 需要先上传 Excel 文件,然后再进行导入,上传 Excel 文件就是 Spring MVC 的知识了,使用 MultipartFile 即可完成。代码如下:

@RestController("")
public class DemoController {

    @PostMapping("importStudent")
    public String importData(MultipartFile file) {
        try {
            EasyExcel.read(file.getInputStream(), Student.class, new ExcelListener())
                    .sheet()
                    .doRead();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return "Success";
    }
}

启动该项目,然后通过 PostMan 来导入前面的 Excel 文件,如下图:

上图中,KEY 的类型选择为 “File”,在控制台中输出结果如下:

表头信息{0=姓名, 1=性别, 2=电话}
Student(name=stu0, gender=0, phone=13912344320)
Student(name=stu1, gender=1, phone=13912344321)
Student(name=stu2, gender=0, phone=13912344322)
Student(name=stu3, gender=1, phone=13912344323)
Student(name=stu4, gender=0, phone=13912344324)
Student(name=stu5, gender=1, phone=13912344325)
Student(name=stu6, gender=0, phone=13912344326)
Student(name=stu7, gender=1, phone=13912344327)
Student(name=stu8, gender=0, phone=13912344328)
Student(name=stu9, gender=1, phone=13912344329)
Student(name=stu0, gender=0, phone=13912344320)
Student(name=stu1, gender=1, phone=13912344321)
Student(name=stu2, gender=0, phone=13912344322)
Student(name=stu3, gender=1, phone=13912344323)
Student(name=stu4, gender=0, phone=13912344324)
Student(name=stu5, gender=1, phone=13912344325)
Student(name=stu6, gender=0, phone=13912344326)
Student(name=stu7, gender=1, phone=13912344327)
Student(name=stu8, gender=0, phone=13912344328)
Student(name=stu9, gender=1, phone=13912344329)

这里沿用了前面导入的 Listener,这里补充一点,EasyExcel 的 Listener 并不被 Spring 进行管理,因此每次使用都需要 new 一个新的实例。那么,如果需要将数据写入数据库,需要通过构造方法,将 Dao 传入,而不能直接将 Dao 进行注入。

当我们导入数据时,不需要逐条将数据写库,频繁的写库效率太低,也不要待数据全部读完再一次写库,如果读入的数量过多时,则会导致占用较大的内存。

在 EasyExcel 官网中给出了一段 Demo 供大家参考,代码我直接复制过来,供大家阅读,代码如下:

// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<DemoData> {
    private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
    /**
     * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 3000;
    /**
     * 缓存的数据
     */
    private List<DemoData> list = new ArrayList<>(BATCH_COUNT);
    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    private DemoDAO demoDAO;

    public DemoDataListener() {
        // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
        demoDAO = new DemoDAO();
    }

    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param demoDAO
     */
    public DemoDataListener(DemoDAO demoDAO) {
        this.demoDAO = demoDAO;
    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(DemoData data, AnalysisContext context) {
        LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
        list.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            list = new ArrayList<>(BATCH_COUNT);
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        LOGGER.info("所有数据解析完成!");
    }

    /**
     * 加上存储数据库
     */
    private void saveData() {
        LOGGER.info("{}条数据,开始存储数据库!", list.size());
        demoDAO.save(list);
        LOGGER.info("存储数据库成功!");
    }
}

代码中有很好的注释,在大家写监听器时,可以充分的进行参考。

六、通过 Web 来导出 Excel 中的数据

同 Excel 的导入相同,实际的 Excel 导出也是通过 Web 页面进行导出的,代码如下:

@GetMapping("exportStudent")
public void exportData(HttpServletResponse response) {
    response.setContentType("application/vnd.ms-excel");
    response.setCharacterEncoding("utf-8");
    String fileName = "student";
    response.setHeader("Content-disposition", "attachment:filename=" + fileName + ".xlsx");

    List<Student> stuList = new ArrayList<>();

    // 实体替换为查询数据库
    for (int i = 0; i < 10; i ++) {
        Student student = new Student();

        student.setName("stu" + i);
        student.setGender(i % 2);
        student.setPhone("1391234432" + i);

        stuList.add(student);
    }

    // 调用方法进行写操作
    try {
        EasyExcel.write(response.getOutputStream(), Student.class)
                .sheet("学生信息").doWrite(stuList);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

代码基本上分为三步,准备导出数据,设置 Http 头,数据导出。这里通过浏览器来访问我们的接口地址,然后 Excel 文件则会顺利的导出。

七、总结

EasyExcel 的使用还是比较简单,功能也比较多,它在导出 Excel 时可以进行合并单元格、设置复杂的表格格式、自定义样式等,还可以提前设置 Excel 的模板,将数据进行填充(填充效果如下图)。在读取 Excel 数据时,可以读取多个 Sheet 页,可以格式化数值、日期等格式。具体可以参考 EasyExcel 的手册和官网,地址如下:

https://alibaba-easyexcel.github.io/index.html

https://github.com/alibaba/easyexcel

本文分享自微信公众号 - 码农UP2U(gh_3c91b47a82e0),作者:码农UP2U

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-09-22

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 自动出报表,零代码做驾驶舱,我找到了Excel的顶级替代

    做了3年报表的我,从只会vlookup开始学Excel,中间也穿插学习了一些VBA和Access。原以为精通Excel可以走天下,但是越到后面越发现遇到了瓶颈。...

    大数据分析不是事儿
  • Spring Boot使用easyexcel实现导入导出Excel

    目标:实现Spring Boot使用easyexcel实现导入导出Excel 工具:IDEA--2020.1 学习目标:Spring Boot使用easyexc...

    背雷管的小青年
  • 条码打印软件如何将excel表导入使用

    在条码打印软件中制作标签的时候,一个一个的制作比较麻烦,我们可以把我们想要的信息保存到txt文本或者excel表中,然后把excel表(txt文本)导入到条码软...

    用户5746110
  • Python|python对Excel读写操作的问题总结

    Excel作为目前最流行的个人计算机数据处理软件,相信大家都使用过,但是在使用excel时,有时长达上千行的数据却让人望而却步,这时候就需要编程来代替我们手动读...

    算法与编程之美
  • 新型肺炎感染人数数据图表化

    点击上方蓝字可直接关注!方便下次阅读。如果对你有帮助,可以点个在看,让它可以帮助到更多同志~

    用户5908113
  • [学员作品]EXCEL催化剂 助力电商数据实现自动化

    在一个比较大的电商公司(小型电商公司一般没有数据岗位配置),负责2个项目的数据,每天处理原始表大概在120个左右,数据主要来源生意参谋,其他平台也有,但是相对较...

    Excel催化剂
  • 从腾讯入职到离职,我仅用了三周:做大数据的同事看不起做报表的

    这是很多年前的事情了,从腾讯入职到离职,我用了三周,理由很简单,做大数据的同事看不起做报表的,当然,我是做报表的那个。

    大数据分析不是事儿
  • 「Sqlserver」数据分析师有理由爱Sqlserver之七-最刚需的数据导入导出功能-导入篇

    在数据消费端,就算是数据分析师的角色,对于正规的公司来说,都不会轻易地开发数据库的访问权限给到终端用户,绝大部分的场景只会是给予导出Excel、csv等文件格式...

    Excel催化剂
  • .NET导出Excel的四种方法及评测

    导出Excel是.NET的常见需求,开源社区、市场上,都提供了不少各式各样的Excel操作相关包。本文,我将使用NPOI、EPPlus、OpenXML、Aspo...

    心莱科技雪雁
  • JeeSite | Excel 导入导出

      在各种管理系统中,数据的导入导出是经常用到的功能,通常导入导出以 Excel、CSV 格式居多。如果是学习的过程中,最好是自己实现数据导入与导出的功能,然而...

    码农UP2U
  • SQL SERVER几种数据迁移/导出导入的实践

    SQLServer提供了多种数据导出导入的工具和方法,在此,分享我实践的经验(只涉及数据库与Excel、数据库与文本文件、数据库与数据库之间的导出导入)。 (一...

    逸鹏
  • Excel文件受损基本急救方法四则

    用户1756920
  • 使用EasyPoi根据权限动态导出列——反射实现

    我们使用的注解版的导出 @Excel,官方文档中说明很详细,默认大家都会用,不会用的,copy一下官网的,跑一下,调一下就行了。

    猿天地
  • 操作MS Execl电子表格的常用JAR包

    支持Excel 95-2000的所有版本;生成Excel 2000标准格式;支持字体、数字、日期操作;能够修饰单元格属性;支持图像和图表。

    java乐园
  • SpringBoot图文教程10—模板导出|百万数据Excel导出|图片导出「easypoi」

    上一篇文章中简单介绍了Poi的使用方式,但是用Poi去写代码着实繁琐了一些,假如你要实现的是复杂的需求,譬如:图片导出,多表数据导出,模板导出,大数据量导出等等...

    鹿老师的Java笔记
  • Python自动化办公知识点整理汇总

    这可能是很多非IT职场人士面临的困惑,想把python用到工作中,却不知如何下手?python在自动化办公领域越来越受欢迎,批量处理简直是加班族的福音。

    墨鬓
  • php导出大量数据到excel使用轻量级的PHP_XLSXWriter比用phpexcel效率更高,2020年9月10日实测!

    在网页应用中我们经常会把数据导出到EXCEL表,之前我一直使用PHPEXCEL作为导出类库,因为它有丰富的EXCEL支持,可以随意调整表格样式。

    睿儿网络郝刚
  • 「补视频」震惊!地图可视化竟能如此玩,零门槛,全免费,效果远胜主流作图工具!

    在笔者所接触到的地图可视化过程中(没有专门深入,欢迎补充),简单介绍下所知道的情形。

    Excel催化剂
  • 导入Excel表格

    string filePath = Server.MapPath("~/Document/Template/考生信息导入模板.xls");

    PHY_68

扫码关注云+社区

领取腾讯云代金券