目录
阿里EasyExcel 即学即会:海量数据的导入导出
一、文章简介
主要讲解在java应用中如何利用EasyExcel技术完成对excel文件的导入和导出操作。
数据从excel------->数据库: 导入
数据从数据库-------->excel:导出
二、概念
1.EasyExcel是什么?
-
EasyExcel是一个基于Java的简单、省内存的读写Excel的阿里开源项目。
-
在尽可能节约内存的情况下支持读写百M的Excel。
2.EasyExcel 能用在哪里?
项目中涉及到Excel文件,CVS文件大多数的读写操作,均可以使用。
3.为什么要选用EasyExcel解析excel?
4.如何使用EasyExcel?
阿里官方文档: EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 官网
三、EasyExcel快速入门
1.环境搭建
-
创建maven工程
-
引入相关坐标
-
<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.1.2</version> </dependency>
-
-
参考官方API完成功能
2.简单写excel
-
编写模型类并加入注解
-
编写获取测试数据的方法
-
调用官方API完成写功能
-
核心代码:
EasyExcel.write(fileName, DemoData.class).sheet("测试").doWrite(data(100000));
代码示例
TestFileUtil
获取代码路径的工具类
package com.ithiema.utils;
/*
获取代码路径的工具类,可以获取到模块这一级的磁盘路径;
*/
public class TestFileUtil {
public static String getPath() {
return TestFileUtil.class.getResource("/").getPath().replace("classes/","");
}
public static void main(String[] args) {
System.out.println(getPath());
}
}
Employee
员工类
package com.ithiema.pojo;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
@ExcelProperty("员工工号")
private int id;
@ExcelProperty("员工姓名")
private String name;
@ExcelProperty("员工工资")
private double salary;
@ExcelProperty("入职日期")
private Date date;
}
SimpleWrite
easyexcel的简单写数据
package com.ithiema.write;
import com.alibaba.excel.EasyExcel;
import com.ithiema.pojo.Employee;
import com.ithiema.utils.TestFileUtil;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/*
练习easyexcel的简单写数据
*/
public class SimpleWrite {
@Test
public void testWrite(){
String name = TestFileUtil.getPath() + "简单写数据" + System.currentTimeMillis() + ".xlsx";
EasyExcel.write(name, Employee.class).sheet("我的快速入门").doWrite(data(12));
}
// 准备测试数据的方法
private List<Employee> data(int i) {
List<Employee> list = new ArrayList<>();
for (int j = 1; j <= i; j++) {
list.add(new Employee(j,"测试数据"+j,6.6*j,new Date()));
}
return list;
}
}
演示效果
3.简单读excel
-
编写模型类并加入注解
-
监听器介绍
-
调用官方API完成写功能
-
核心代码:
EasyExcel.read(fileName, DemoData.class, new PageReadListener<DemoData>
(list -> System.out.println(list))).sheet().doRead();
代码示例
SimpleRead
快速入门,简单读
package com.ithiema.read;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.listener.PageReadListener;
import com.ithiema.pojo.Employee;
import com.ithiema.utils.TestFileUtil;
import org.junit.Test;
import java.io.File;
/*
快速入门,简单读
*/
public class SimpleRead {
/**
* 使用EasyExcel框架读取Excel文件
*
* 指定文件路径,数据模型类(Employee.class),以及一个读取监听器,用于处理读取到的每条数据。
* 文件名通过TestFileUtil.getPath()获取相对路径,并拼接上特定的文件名。
* 使用EasyExcel的read方法指定文件名、数据模型类和监听器,然后调用sheet().doRead()开始读取第一个工作表的数据。
*/
@Test
public void testRead(){
// 构建文件路径
String fileName = "D:\\develop\\dev\\easyexcel-course\\easyExcel-heima\\QuickStart\\target\\test-简单写数据1722256409955.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
// 这里每次会读取100条数据 然后返回过来 直接调用使用数据就行
EasyExcel.read(fileName, Employee.class, new PageReadListener<Employee>(dataList -> {
// 遍历并打印读取到的每个员工对象
for (Employee employee : dataList) {
System.out.println(employee);
}
})).sheet(0).doRead();
}
}
演示效果
四、EasyExcel进阶操作
1.批量(重复)写数据
-
编写模型类并加入注解
-
调用官方API完成写功能
try (ExcelWriter writer = EasyExcel.write(fileName, DemoData.class).build()) {
WriteSheet writeSheet = EasyExcel.writerSheet("测试表1").build();
for (int i = 0; i < 3; i++) {
ExcelWriter write = writer.write(data(100000), writeSheet);
}
}
代码示例
ManyWrite
进阶写数据
package com.ithiema.write;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.ithiema.pojo.Employee;
import com.ithiema.utils.TestFileUtil;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/*
进阶写数据
*/
public class ManyWrite {
/**
* 批量写入Excel数据
*
* 该方法创建一个Excel文件,然后分三次写入100条员工数据。
* 使用EasyExcel框架简化了Excel文件的写入操作。
*/
@Test
public void testWrite(){
// 构建文件名,包含批量写数据字样和当前时间戳,确保文件名的唯一性
String name = TestFileUtil.getPath() + "批量写数据" + System.currentTimeMillis() + ".xlsx";
// 与文件想关联的写对象 使用try-with-resources语句确保ExcelWriter资源在使用后能被正确关闭
try (ExcelWriter writer = EasyExcel.write(name, Employee.class).build()) {
// 准备一个表对象 设置写入的Sheet信息
WriteSheet sheet = EasyExcel.writerSheet("批量写练习").build();
// 循环三次,每次写入10000条员工数据
// 利用writer向sheet表写数据
for (int i = 1; i <= 3; i++) {
writer.write(data(100), sheet);
}
}
}
// 准备测试数据的方法
private List<Employee> data(int i) {
List<Employee> list = new ArrayList<>();
for (int j = 1; j <= i; j++) {
list.add(new Employee(j,"测试数据"+j,6.6*j,new Date()));
}
return list;
}
}
效果演示
上面写的数据格式不好看怎么办?
-
利用阿里提供的模版写数据
-
模版设置的样式新添加的数据会自动包含样式
2.按模版填充单个对象数据
-
编写模型类并加入注解
-
按要求编写模版文件
-
调用官方API完成写功能
EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData);
代码示例
效果演示
3.按模版批量填充多个对象数据
-
编写模型类并加入注解
-
按要求编写模版文件
-
调用官方API完成写功能
try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()) {
WriteSheet writeSheet = EasyExcel.writerSheet(0).build();
WriteSheet writeSheet2 = EasyExcel.writerSheet("数据2").build();
excelWriter.fill(data(300000), writeSheet);
excelWriter.fill(data(300000), writeSheet2);
}
代码示例
package com.ithiema.write;
import com.alibaba.excel.EasyExcel;
import com.ithiema.pojo.Employee;
import com.ithiema.utils.TestFileUtil;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/*
根据模版写数据
*/
public class FillWriter {
/**
* 使用EasyExcel框架通过模板填充数据至新Excel文件
*
* 此方法将从指定的模板文件读取样式和结构,然后填充20条数据(由data(20)方法提供)到名为"1025"的工作表中,
* 并保存到新的Excel文件中,文件名中包含系统当前时间戳以确保唯一性。
*/
@Test
public void testFill(){
// 模板文件的绝对路径
String templateFileName = "D:\\develop\\dev\\easyexcel-course\\easyExcel-heima\\QuickStart\\target\\模板.xlsx";
// 目标文件的绝对路径,包含时间戳确保文件名唯一
String fileName = "D:\\develop\\dev\\easyexcel-course\\easyExcel-heima\\QuickStart\\target\\"+System.currentTimeMillis()+ "按照模版写数据.xlsx";
// 使用EasyExcel框架,指定目标文件、模板文件和工作表名称进行数据填充
EasyExcel.write(fileName).withTemplate(templateFileName).sheet("1025").doFill(data(20));
}
// 准备测试数据的方法
private List<Employee> data(int i) {
List<Employee> list = new ArrayList<>();
for (int j = 1; j <= i; j++) {
list.add(new Employee(j,"测试数据"+j,6.6*j,new Date()));
}
return list;
}
}
4.自定义监听器读海量(百万级别)数据并监控内存消耗
-
编写模型类并加入注解
-
自定义监听器
-
调用官方API完成写功能
EasyExcel.read(fileName, IndexOrNameData.class, new IndexOrNameDataListener()).sheet().doRead();
代码示例
EmployeeDao
package com.ithiema.dao;
import com.ithiema.pojo.Employee;
import java.util.List;
public class EmployeeDao {
public void saveData(List<Employee> list){
System.out.println("模拟保存数据..."+list.size());
}
}
EmployeeListener
/**
* EmployeeListener 类实现了 ReadListener 接口,用于处理 Excel 文件读取过程中的数据事件。
* 主要功能是在读取每行数据时将其添加到一个列表中,当列表达到预设大小时批量保存数据到数据库,
* 并在读取完成后处理剩余未达批次的数据。
*/
public class EmployeeListener implements ReadListener<Employee> {
/**
* 批量保存数据的阈值,默认为 100 条记录。
*/
private int count = 100;
/**
* 数据访问对象,用于与数据库交互,执行数据保存操作。
*/
private EmployeeDao dao;
/**
* 临时存储读取数据的列表,达到预设大小后清空并批量保存。
*/
private List<Employee> list = new ArrayList<>(count);
/**
* 构造函数,初始化数据访问对象。
*
* @param dao 数据访问对象,用于数据的持久化操作。
*/
public EmployeeListener(EmployeeDao dao) {
this.dao = dao;
}
/**
* 计数器,用于监控批量保存次数,每 100 批次调用一次垃圾回收。
*/
int c = 0;
/**
* 当解析一行数据时调用此方法。
* 将读取到的 Employee 对象添加到 list 中,当 list 达到预设大小时,调用 dao 的 saveData 方法批量保存数据,
* 并重新初始化 list。
*
* @param employee 解析得到的 Employee 对象。
* @param analysisContext 分析上下文,包含解析过程中的相关信息。
*/
@Override
public void invoke(Employee employee, AnalysisContext analysisContext) {
list.add(employee);
if(list.size() >= count){
dao.saveData(list);
list = new ArrayList<>(count);
c++;
if(c % 100 == 0){
System.gc(); // 每 100 批次调用一次垃圾回收,优化内存使用。
}
}
}
/**
* 在所有数据解析完成后调用此方法。
* 处理剩余未达批次的数据,即当读取完成时 list 中可能还存在未保存的数据,此时将它们全部保存到数据库。
*
* @param analysisContext 分析上下文,包含解析过程中的相关信息。
*/
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
if(list.size() > 0){
dao.saveData(list); // 保存剩余数据。
}
}
}
MyRead
package com.ithiema.read;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.ithiema.EmployeeListener.EmployeeListener;
import com.ithiema.dao.EmployeeDao;
import com.ithiema.pojo.Employee;
import com.ithiema.utils.TestFileUtil;
import org.junit.Test;
/*
使用自定义监听器读数据
*/
public class MyRead {
// 读单表
@Test
public void testRead(){
String filename = TestFileUtil.getPath() + "3万.xlsx";
ExcelReader reader = EasyExcel.read(filename, Employee.class,new EmployeeListener(new EmployeeDao())).build();
ReadSheet sheet = EasyExcel.readSheet().build();
reader.read(sheet);
}
@Test
public void testRead3(){
String filename = TestFileUtil.getPath() + "批量写数据1669108667435.xlsx";
EasyExcel.read(filename, Employee.class, new EmployeeListener(new EmployeeDao())).sheet().doRead();
}
}
五、EasyExcel综合应用
-
环境搭建
-
实现文件导入功能(采用异步上传并使用遮罩层)
-
实现文件导出功能(采用同步下载并解决文件名中文乱码)
代码gitee地址: