Alibaba EasyExcel初体验

2021年09月15日 阅读数:2
这篇文章主要向大家介绍Alibaba EasyExcel初体验,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

简介

EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽量节约内存的状况下支持读写百M的Excel。相对于Apache POI来讲,EasyExcel是从磁盘上一行行的读取数据,而后逐个解析,避免将大量数据加载到内存从而致使OOM。java

相关依赖:web

	com.alibaba
	easyexcel
	2.2.6
	org.projectlombok
	lombok
	1.18.12
	provided

Excel导入

须要导入的数据:
Alibaba EasyExcel初体验_Excelspring

Model类:app

@Data
public class SchoolData {

    @ExcelProperty(value = "地址")
    private String address;

    @ExcelProperty(value = "学校名称")
    private String schoolName;

    @ExcelProperty(value = "邮政编码")
    private String postcode;

    @ExcelProperty(value = "收集时间")
    private Date collectDate;
}

使用@ExcelProperty注解时,可使用两种方式将数据列和属性进行绑定:ide

  1. 经过value指定名称,将名称对应的excel列的数据绑定到属性。
  2. 经过index指定下标,将下标对应的excel列的数据绑定到属性。

在不使用@ExcelProperty注解时,默认Model类属性和excel列数据按照前后顺序进行绑定的。post

监听器:测试

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class SchoolDataListener extends AnalysisEventListener{

    ListschoolDataList = new ArrayList<>();

    //须要传参时,经过构造方法传进来
    public SchoolDataListener() {
    }

    /**
     * 每解析一行数据都会进行调用
     */
    @Override
    public void invoke(SchoolData data, AnalysisContext context) {
        //通常数据量大的时候应该分批处理,防止数据所有加载到内存,致使OOM。
        schoolDataList.add(data);
    }

    /**
     * 数据所有解析完成时调用
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        System.out.println("数据读取完毕:");
        schoolDataList.stream().forEach(System.out::println);
    }

    /**
     * 每解析一行表头都会进行调用
     */
    @Override
    public void invokeHeadMap(MapheadMap, AnalysisContext context) {
        System.out.println("表头:");
        headMap.forEach((k, v) -> System.out.println(v));
        System.out.println();
    }
}

导入测试:ui

导入有如下两种写法,通常使用简写的方式就行,当须要实现复杂的功能时可能会须要另一种复杂的写法。编码

@Test
public void importExcel() {
	ExcelReader excelReader = null;
	try {
		excelReader = EasyExcel.read("F:\\测试数据.xlsx", SchoolData.class, new SchoolDataListener()).build();
		ReadSheet readSheet = EasyExcel.readSheet(0).build();
		excelReader.read(readSheet);
	} finally {
		if (excelReader != null) {
			// 这里千万别忘记关闭,读的时候会建立临时文件,到时磁盘会崩的
			excelReader.finish();
		}
	}
}

@Test
public void importExcelSimple() {
	//sheet() 默认读取第一个sheet
	EasyExcel.read("F:\\测试数据.xlsx", SchoolData.class, new SchoolDataListener()).sheet().doRead();
}

输出结果:.net

表头:
学校名称
地址
邮政编码
收集时间

数据读取完毕:
SchoolData(address=晋宁县古城镇旧寨村  , schoolName=晋宁县古城镇古城中学, postcode=650604, collectDate=Sun Jan 05 00:00:00 CST 2020)
SchoolData(address=云南省昆明市晋宁县二街乡老高村  , schoolName=晋宁县二街中学, postcode=650608, collectDate=Mon Jan 06 00:00:00 CST 2020)
SchoolData(address=云南省昆明市晋宁县晋城镇寺林街  , schoolName=晋宁县晋城中学, postcode=650605, collectDate=Tue Jan 07 00:00:00 CST 2020)
SchoolData(address=云南省晋宁县六街乡中学  , schoolName=晋宁县六街中学, postcode=650609, collectDate=Wed Jan 08 00:00:00 CST 2020)
SchoolData(address=云南省昆明市晋宁县新街乡文河村  , schoolName=晋宁县新街中学, postcode=650606, collectDate=Thu Jan 09 00:00:00 CST 2020)
SchoolData(address=晋宁县中和乡普照路  , schoolName=晋宁县中和中学, postcode=650600, collectDate=Fri Jan 10 00:00:00 CST 2020)
SchoolData(address=云南省昆明市晋宁县上蒜乡上蒜中学  , schoolName=晋宁县上蒜乡上蒜中学, postcode=650607, collectDate=Sat Jan 11 00:00:00 CST 2020)
SchoolData(address=云南省昆明市晋宁县化乐乡化乐村  , schoolName=晋宁县化乐乡中学, postcode=650611, collectDate=Sun Jan 12 00:00:00 CST 2020)
SchoolData(address=晋宁县夕阳乡夕阳街夕阳民族中学  , schoolName=晋宁县夕阳民族中学, postcode=650603, collectDate=Mon Jan 13 00:00:00 CST 2020)
SchoolData(address=云南省昆明市晋宁县宝峰镇古城村委会小古城村  , schoolName=晋宁县宝峰镇宝峰中学, postcode=650601, collectDate=Tue Jan 14 00:00:00 CST 2020)
SchoolData(address=晋宁县双河乡椿树营  , schoolName=晋宁县双河民族中学, postcode=650602, collectDate=Wed Jan 15 00:00:00 CST 2020)
SchoolData(address=昆明市富民县永定镇环城南路7号永定中学  , schoolName=富民县永定中学, postcode=650400, collectDate=Thu Jan 16 00:00:00 CST 2020)
SchoolData(address=富民县永定镇大西山村  , schoolName=富民县勤劳中学, postcode=650400, collectDate=Fri Jan 17 00:00:00 CST 2020)
SchoolData(address=富民县大营镇大营街16号  , schoolName=富民县大营中学, postcode=650400, collectDate=Sat Jan 18 00:00:00 CST 2020)
SchoolData(address=云南省昆明市罗免乡者北村委会者北街  , schoolName=富民县第二中学, postcode=650401, collectDate=Sun Jan 19 00:00:00 CST 2020)

Excel导出

模拟生成数据:

public class DataGenerate {

    /**
     * 数据生成
     */
    public static Listdata() {
        ListschoolDataList = new ArrayList<>();
        SchoolData schoolData;
        for (int i = 0; i < 15; i++) {
            schoolData = new SchoolData();
            schoolData.setSchoolName("第" + i + "XXX中学");
            schoolData.setAddress("云南省昆明市XXXXX");
            schoolData.setPostcode("123456");
            schoolData.setCollectDate(new Date());
            schoolDataList.add(schoolData);
        }

        return schoolDataList;
    }
}

测试导出:

同导入,导出也有如下两种写法。LongestMatchColumnWidthStyleStrategy用来设置自动列宽,若是须要设置列宽、行高等属性,能够在Model类上添加对应注解进行设置,如@ColumnWidth、@ContentRowHeight、@HeadRowHeight等等。

@Test
public void exportExcel() {
	ExcelWriter excelWriter = null;
	try {
		excelWriter = EasyExcel.write("F:\\测试数据_副本.xlsx", SchoolData.class).build();
		WriteSheet writeSheet = EasyExcel.writerSheet("学校数据").registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();
		excelWriter.write(DataGenerate.data(), writeSheet);
	} finally {
		// 千万别忘记finish 会帮忙关闭流
		if (excelWriter != null) {
			excelWriter.finish();
		}
	}
}

@Test
public void exportExcelSimple() {
	EasyExcel.write("F:\\测试数据_副本.xlsx", SchoolData.class).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("学校数据").doWrite(DataGenerate.data());
}

导出结果:
Alibaba EasyExcel初体验_Excel_02

基于Excel模板的填充

模板:
Alibaba EasyExcel初体验_Excel_03

{} 表明普通变量 {.} 表明是list的变量。

模板填充测试:

@Test
public void excelTemplateFill() {
	ExcelWriter excelWriter = EasyExcel.write("F:\\测试数据_副本3.xlsx").withTemplate("F:\\模板.xlsx").build();
	WriteSheet writeSheet = EasyExcel.writerSheet().build();
	FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();

	//填充列表数据
	excelWriter.fill(DataGenerate.data(), fillConfig, writeSheet);

	//填充普通数据
	Mapmap = new HashMap();
	map.put("name", "狗子");
	map.put("date", "2021-02-19");
	excelWriter.fill(map, writeSheet);
	excelWriter.finish();
}

填充结果:
Alibaba EasyExcel初体验_Excel_04

Web中的导入和导出

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;

@RestController
public class SchoolDataController {

    /**
     * 导入Excel
     */
    @PostMapping("/import")
    public String importExcel(MultipartFile file) throws IOException {
        EasyExcel.read(file.getInputStream(), SchoolData.class, new SchoolDataListener()).sheet().doRead();
        return "success";
    }

    /**
     * 导出Excel
     */
    @GetMapping("/export")
    public void exportExcel(HttpServletResponse response) throws IOException {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode能够防止中文乱码 固然和easyexcel没有关系
        String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        // 这里须要设置不关闭流
        EasyExcel.write(response.getOutputStream(), SchoolData.class).autoCloseStream(Boolean.FALSE).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("学校数据").doWrite(DataGenerate.data());
    }
}