Skip to content

1 导出Excel

wangguanquan3 edited this page May 25, 2022 · 25 revisions

下面会介绍如何使用EEC来写excel文件,EEC目前支持ListSheetListMapSheetStatementSheetResultSetSheetEmptySheet几种内置的Worksheet,如果不能满足需求你也可以继承已有的Worksheet来扩展,最常见的就是对于大数据量写入时的分片处理,这个在后面会讲到,目前还是从最简单的ListSheet出发。

如何将数据导出到excel

数据导出应该是开发过程中比较常见的功能,就是这种简单功能如果使用Apache POI来开发可不是一件轻松的活,幸好EEC已经为我们做了大量的封装,使我们可以做到开箱即用,下面代码展示如何开发简单的对象数组导出功能

/**
 * 导出学生信息
 */
public void exportStudent(List<Student> students) throws IOException {
    new Workbook("二年级学生表") // 新增一个Workbook并指定名称,也就是Excel文件名
        .addSheet(new ListSheet<>(students)) // 添加一个Sheet页,并指定导出数据
        .writeTo(Paths.get("e:/excel")); // 指定导出位置
}

以上writeTo方法指定一个输出位置,不需要指定具体文件名称,名称在实例化Workbook时指定,如果未指定则默认使用“新建文件”做为文件名,另外writeTo是终止符,调用该方法将触发写操作,在其后设置的所有属性将不生效。

为了数据安全,EEC默认只会导出标记有@ExcelColumn的属性,如果Student对象里未标记@ExcelColumn那上面代码与EmptySheet等效。如果是做web开发则可以将writeTo切换输出到Response的Outputstream中,如下代码

/**
 * 直接将excell输出到流
 */
@GetMapping("/download")
public void download(HttpServletResponse response) throws IOException {
    String fileName = java.net.URLEncoder.encode("新建文件.xlsx", "UTF-8");
    response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"; filename*=utf-8''" + fileName);
    // 查询数据
    List<Student> students = studentService.list();

    new Workbook().addSheet(new ListSheet<>(students)).writeTo(response.getOutputStream());
}

当然也可以强制导出,只需要调用forceExport方法就可以导出所有字段(标记了@IgnroeExport注解除外)

如何添加多个Worksheet

EEC是通过Workbook#addSheet方法添加Worksheet,添加的时候你可以指定Sheet的名称,如果不指定则默认使用Sheet {N}命名。对于导出多个Sheet页只需要多调用几次addSheet方法即可,非常方便。 另外,添加顺序决定导出时各Sheet顺序,如果想调整此顺序可以调用Workbook#insertSheet方法插入到指定下标(从0开始),与普通的Array操作一样。

下面代码演示生成多个Worksheet

public void multiSheet() throws IOException {
    new Workbook("multiSheet")
        .addSheet(new ListSheet<>("帐单表", checksTestData()))
        .addSheet(new ListSheet<>("客户表", customersTestData()))
        .addSheet(new ListSheet<>("用户客户关系表", c2CSTestData()))
        .writeTo(Paths.get("e:/excel"));
}

导出文件如下:

multi-sheet.png

如何隐藏某些Sheet

出于某些安全考虑需要隐藏某个或者某些Sheet页该如何处理呢?答案是只需要在对应的Sheet上调用#hidden()方法。调用该方法后数据依然会正常写出,只是该页被隐藏。

下面代码演示隐藏某个Worksheet

public void multiSheet() throws IOException {
    new Workbook("multiSheet")
        .addSheet(new ListSheet<>("帐单表", checksTestData()))
        .addSheet(new ListSheet<>("客户表", customersTestData()).hidden()) // 隐藏该Sheet
        .addSheet(new ListSheet<>("用户客户关系表", c2CSTestData()))
        .writeTo(Paths.get("e:/excel"));
}

导出文件如下,点击右键选择“取消隐藏”就可以还原了:

hidden-sheet.png

关于自动分页

单个worksheet页有行数上限,xls上限为65,536,xlsx上限为1,048,576,如果数据超过该如何处理呢,需要手动进行截取么,还是抛异常?

EEC是为大数据量而生,所以自然考虑到了这种情况,当数据量超过单sheet上限时会自动进行分页处理,无须用户额外处理,而大多数同类工具均是直接抛异常。

自动分页部分代码解析

    /**
     * Split worksheet data
     */
    @Override
    protected void paging() {
        // dataSize()是当前一组数据块的大小,limit是获取单个worksheet的行上限
        int len = dataSize(), limit = getRowLimit();
        // paging
        if (len + rows > limit) {
            // Reset current index
            end = limit - rows + start; // end是标记dataSize的最后位置,因为已经超限了所以会当前页只会取未超限的数据
            shouldClose = false;
            eof = true;
            size = limit;

            int n = id;
            for (int i = end; i < len; ) {
                @SuppressWarnings("unchecked")
                ListSheet<T> copy = getClass().cast(clone()); // 复制一个新的worksheet
                copy.start = i;
                copy.end = (i = Math.min(i + limit, len));
                copy.size = copy.end - copy.start;
                copy.eof = copy.size == limit;
                workbook.insertSheet(n++, copy); // 插入到当前worksheet后面
            }
            // Close on the last copy worksheet
            workbook.getSheetAt(n - 1).shouldClose = true; // 如果是最后一个分页则关闭
        } else {
            end = len;
            size += len;
        }
    }

简单数据类型导出

有时候仅仅想导出最简单的数据类型,比如Integer,String,如果定义实体就显得过度设计,那应该如何处理呢?

public void testBasicType() throws IOException {
    List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,0);
    new Workbook("Integer array").addSheet(new ListSheet<Integer>(list) {
        @Override
        public org.ttzero.excel.entity.Column[] getHeaderColumns() {
            return new org.ttzero.excel.entity.Column[]{ new ListSheet.EntryColumn("Number").setClazz(Integer.class) };
        }
    }).writeTo(defaultTestPath);
}

查看更多 高级特性

Clone this wiki locally