欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

阿里开源(EasyExcel)---导出EXCEL

程序员文章站 2022-07-13 15:55:00
...

一. 简介

         导出是后台管理系统的常用功能,当数据量特别大的时候会内存溢出和卡顿页面,曾经自己封装过一个导出,POI百万级大数据量EXCEL导出 采用了分批查询数据来避免内存溢出和使用SXSSFWorkbook方式缓存数据到文件上以解决下载大文件EXCEL卡死页面的问题。不过一是存在封装不太友好使用不方便的问题,二是这些poi的操作方式仍然存在内存占用过大的问题,以及存在内存溢出的隐患。无意间查询到阿里开源的EasyExcel框架,发现可以将解析的EXCEL的内存占用控制在KB级别,并且绝对不会内存溢出(内部实现待研究),还有就是速度极快, 大概100W条记录,十几个字段, 只需要70秒即可完成下载。遂抛弃自己封装的,转战研究阿里开源的EasyExcel. 不过 说实话,当时自己封装的那个还是有些技术含量的,例如 外观模式,模板方法模式,以及委托思想,组合思想,可以看看。

         EasyExcel的github地址是:https://github.com/alibaba/easyexcel 

二. 案例

2.1 POM依赖

        <!-- 阿里开源EXCEL -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>1.1.1</version>
        </dependency>

2.2 POJO对象

package com.authorization.privilege.excel;

import java.util.Date;

/**
 * @author qjwyss
 * @date 2019/3/15
 * @description
 */
public class User {

    private String uid;
    private String name;
    private Integer age;
    private Date birthday;

    public User() {
    }

    public User(String uid, String name, Integer age, Date birthday) {
        this.uid = uid;
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    public String getUid() {
        return uid;
    }

    public void setUid(String uid) {
        this.uid = uid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

2.3 测试

2.3.1 数据量少的:一个SHEET一次查询导出

/**
     * 针对较少的记录数可以调用该方法一次性查出然后写入到EXCEL的一个SHEET中
     * 注意: 一次性查询出来的记录数量不宜过大,不会内存溢出即可。
     *
     * @throws IOException
     */
    @Test
    public void writeExcelOneSheetOnce() throws IOException {

        // 生成EXCEL并指定输出路径
        OutputStream out = new FileOutputStream("E:\\temp\\withoutHead1.xlsx");
        ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX);

        // 设置SHEET
        Sheet sheet1 = new Sheet(1, 0);
        sheet1.setSheetName("sheet1");

        // 设置标题
        Table table = new Table(1);
        List<List<String>> titles = new ArrayList<List<String>>();
        titles.add(Arrays.asList("用户ID"));
        titles.add(Arrays.asList("名称"));
        titles.add(Arrays.asList("年龄"));
        titles.add(Arrays.asList("生日"));
        table.setHead(titles);

        // 查询数据导出即可
        List<List<String>> userList = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            userList.add(Arrays.asList("ID_" + i, "小明" + i, String.valueOf(i), new Date().toString()));
        }

        writer.write0(userList, sheet1, table);
        writer.finish();
    }

 

2.3.2 数据量适中: 一个SHEET分批查询导出

 /**
     * 针对106W以内的记录数可以调用该方法分多批次查出然后写入到EXCEL的一个SHEET中
     * 注意:
     * 每次查询出来的记录数量不宜过大,根据内存大小设置合理的每次查询记录数,不会内存溢出即可。
     * 数据量不能超过一个SHEET存储的最大数据量105W
     *
     * @throws IOException
     */
    @Test
    public void writeExcelOneSheetRepeatly() throws IOException {

        // 生成EXCEL并指定输出路径
        OutputStream out = new FileOutputStream("E:\\temp\\withoutHead2.xlsx");
        ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX);

        // 设置SHEET
        Sheet sheet1 = new Sheet(1, 0);
        sheet1.setSheetName("sheet1");

        // 设置标题
        Table table = new Table(1);
        List<List<String>> titles = new ArrayList<List<String>>();
        titles.add(Arrays.asList("用户ID"));
        titles.add(Arrays.asList("名称"));
        titles.add(Arrays.asList("年龄"));
        titles.add(Arrays.asList("生日"));
        table.setHead(titles);

        // 模拟分批查询:总记录数50条,每次查询20条,  分三次查询
        Integer totalRowCount = 50;
        Integer pageSize = 20;
        Integer writeCount = totalRowCount % pageSize == 0 ? (totalRowCount / pageSize) : (totalRowCount / pageSize + 1);

        for (int i = 0; i < writeCount - 1; i++) {

            // 此处到时候可以使用DAO分批查询即可
            List<List<String>> userList = new ArrayList<>();
            for (int j = i * pageSize + 1; j <= (i + 1) * pageSize; j++) {
                userList.add(Arrays.asList("ID_" + j, "小明", String.valueOf(j), new Date().toString()));
            }
            writer.write0(userList, sheet1, table);

        }

        if (totalRowCount % pageSize != 0) {
            List<List<String>> userList = new ArrayList<>();
            for (int k = (writeCount - 1) * pageSize + 1; k <= totalRowCount; k++) {
                userList.add(Arrays.asList("ID_" + k, "小明", String.valueOf(k), new Date().toString()));
            }
            writer.write0(userList, sheet1, table);
        }

        writer.finish();
    }

 

2.3.3 数据里很大: 多个SHEET分批查询导出 TODO 

 

 

 

三. 实际使用 TODO