场景

  1. 最近在开发JavaWeb网站时,有时候需要导出查询的数据为Excel数据. 使用POI库的话需要自己写每一列每一行,很麻烦. 如何能快速编码生成一个带表格数据Excel文件,但是性能也需要足够高,不能耗费太多内存。阿里的开源库easyexcel可以做到.
  2. easyexcel是基于Java对象的成员变量映射到每行数据的,而大部分例子都是通过配置成员变量的注解属性来确定列名,忽略的列,合并列,字段输出格式等。那么如果不想用注解来说明成员变量,有什么办法?

说明

  1. easyexcel这种基于Java对象输出数据到xlsx文件上也是有缺陷的,因为有些查询的数据并不是基于settergetter方式的,最好能提供一个接口,类似Cocoa里的TableView, 可以重载一个代理,而这里代理有只有在显示数据时才会通过代理调用get方法取某行某列的数据,而不管原数据元如何存储.
# 伪代码
string get(int column,int row)
  1. 模块化开发时,如果使用easyexcelJava对象注解,那么就会对easyexcel有依赖,而用到这些对象的其他模块也会对easyexcel产生依赖。所以,不建议使用注解的方式来声明列属性。所幸easyexcel也提供了List作为参数对每列名进行声明的head()方法.
  2. easyexcel也有包含需要输出的成员变量名includeColumnFiledNames()或排除输出的成员变量名excludeColumnFiledNames方法.
  3. 常见的输出到文件的做法是传入一个文件字符串路径参数EasyExcel.write(fileName)创建一个ExcelWriterBuilder, 但是也支持传入流. 比如使用jfinal JavaWeb框架时,需要下载excel文件,那么可以通过传入输出流EasyExcel.write(response.getOutputStream())来直接输出,而不需要生成临时文件。有一点要注意, 在有些框架如jfinal里,输出流的关闭是由框架最后进行关闭的,而easyexcel默认会在数据输出完之后关闭输出流,所以会出问题。easyexcel也提供了一个设置是否自动关闭流的方法,可以设置为false, 这样框架关闭输出流时不会出问题.
EasyExcel.write(response.getOutputStream()).autoCloseStream(false)

例子

  1. 这里有生成文件和输出到流的例子,如果是想让easyexcel自动关闭输出流,那么方法autoCloseStream可以不调用.

pom.xml 依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.0.5</version>
</dependency>

生成文件

  1. 输出到临时文件夹的simple-excel.xlsx里.

DemoData.java

package com.example.excel;

public class DemoData {

    private String string;
    private Double doubleData;
    
    public String getString() {
        return string;
    }
    public void setString(String string) {
        this.string = string;
    }
    public Double getDoubleData() {
        return doubleData;
    }
    public void setDoubleData(Double doubleData) {
        this.doubleData = doubleData;
    }

}

ExcelTest.java

package com.example.excel;

import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.alibaba.excel.EasyExcel;

import org.junit.jupiter.api.Test;

public class ExcelTest {
    
    public void print(String key,String str){
        System.out.println("*"+key+"*: ["+str+"]");
    }

    @Test
    public void testNativeExcelGenerate(){
        print("Excel","BEGIN");

        //URL resource = ExcelTest.class.getResource("");
        //print("Dir",resource.getPath());

        String tempPath =System.getProperty("java.io.tmpdir");
        String fileName = tempPath+"simple-excel.xlsx";
        print("FileName",fileName);

        Set<String> excludeColumnFiledNames = new HashSet<>();
        excludeColumnFiledNames.add("attrs");
        excludeColumnFiledNames.add("modifyFlag");
        excludeColumnFiledNames.add("configName");

        Set<String> includeColumnFiledNames = new HashSet<>();
        includeColumnFiledNames.add("string");
        includeColumnFiledNames.add("doubleData");

        List<String> stringTitle = new ArrayList<>();
        stringTitle.add("字符串");

        List<String> doubleDateTitle = new ArrayList<>();
        doubleDateTitle.add("数值");
        List<List<String>> list = new ArrayList<List<String>>();
        list.add(stringTitle);
        list.add(doubleDateTitle);

        EasyExcel.write(fileName)
                .excludeColumnFiledNames(excludeColumnFiledNames)
                .includeColumnFiledNames(includeColumnFiledNames)
                .sheet("模板")
                .head(list)
                .doWrite(() -> {
                    // 分页查询数据
                    return data();
                });
        
        print("Excel","END");
    }

    private List<DemoData> data() {
        List<DemoData> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            DemoData data = new DemoData();
            data.setString("字符串" + i);
            data.setDoubleData(0.56+i);
            list.add(data);
        }
        return list;
    }
}

输出到流

  1. 部分参数创建省略,和输出到文件基本一样.
HttpServletResponse response = oc.getResponse();
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
List<List<String>> headers = new ArrayList<List<String>>();
for (String one :tableHeaders) {
    List<String> l1 = new ArrayList<>();
    l1.add(one);
    headers.add(l1);
}

Set<String> excludeColumnFiledNames = new HashSet<>();
excludeColumnFiledNames.add("attrs");
excludeColumnFiledNames.add("modifyFlag");
excludeColumnFiledNames.add("configName");

boolean result = false;
try {
    String fileNameNew = URLEncoder.encode(fileName, "UTF-8");
    response.setHeader("Content-disposition", "attachment;filename=" + fileNameNew + ".xlsx");

    EasyExcel.write(response.getOutputStream()).autoCloseStream(false)
            .excludeColumnFiledNames(excludeColumnFiledNames)
            .includeColumnFiledNames(fields)
            .head(headers).sheet(fileName).doWrite(items);
    result = true;
} catch (UnsupportedEncodingException e) {
    logger.error("Excel encode fileName error "+fileName,e.getCause());
} catch (IOException e) {
    logger.error("Response getOutputStream fail ",e.getCause());
}

参考

  1. easyexcel 写excel
  2. easyexcel