Excel大数据量导入导出
github源码
地址(更详细)
:
https://github.com/alibaba/easyexcel
文档:读Excel(文档已经迁移)
B
站视频
:
https://www.bilibili.com/video/BV1Ff4y1U7Qc
一、JAVA解析EXCEL工具EasyExcel
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的AP可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POl sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POl的sax模式。在上层做了模型转换的封装,让使用者更加简单方便
64M内存1分钟内读取75M(46W行25列)的Excel
二、EasyExcel的使用
EasyExcel的相关依赖
添加
maven
依赖
<!-- Alibaba Excel 依赖 -->
<!--它提供了高性能的读写功能,特别适合处理大型 Excel 文件 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.4</version>
</dependency>
<!-- Apache POI 依赖 -->
<!--poi 是 Apache POI 项目的核心库,提供了读写 Microsoft Office 格式文件的基础功能,包括 .xls 文件 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>4.1.2</version>
</dependency>
<!-- SLF4J 和 Logback 依赖 -->
<!-- 日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
三、写Excel
创建实体类
@ExcelProperty
注解的
value
属性是一个数组类型
,
设置多个
head
时会自动合并,用于指定Excel表格中该字段的显示名称(起名字)
@NoArgsConstructor
和
@AllArgsConstructor
是
Lombok
库提供的注解
@NoArgsConstructor
生成一个无参构造函数(即没有参数的构造函数)
@AllArgsConstructor
生成一个全参构造函数(即包含类中所有字段的构造函数)
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class User {
@ExcelProperty(value = "用户编号")
private Integer userId;
@ExcelProperty(value = "姓名")
private String userName;
@ExcelProperty(value = "性别")
private String gender;
@ExcelProperty(value = "工资")
private Double salary;
@ExcelProperty(value = "入职时间")
private Date hireDate;
// lombok 会生成getter/setter方法
}
1.简单写入方法一
![](https://i-blog.csdnimg.cn/direct/69a6869326ca45eb8e6093391f037664.png)
/**
* 简单写方法一
*/
@Test
public void text01(){
//创建一个Excel文档
String fileName = "D:\\excel\\user1.xlsx";
//根据user模板构建数据
List<User> users = new ArrayList<>();
User user1 = new User(1,"张三","男",666.66,new Date());
User user2 = new User(2,"张三","男",666.66,new Date());
User user3 = new User(3,"张三","男",666.66,new Date());
User user4 = new User(4,"张三","男",666.66,new Date());
users.add(user1);
users.add(user2);
users.add(user3);
users.add(user4);
//向Excel表格中写数据
EasyExcel.write(fileName,User.class).sheet("用户信息").doWrite(users);
}
2.简单写入方法二
/**
* 简单写方法二
*/
@Test
public void text02(){
//创建一个Excel文档
String fileName = "D:\\excel\\user2.xlsx";
//根据user模板构建数据
List<User> users = new ArrayList<>();
User user1 = new User(1,"张三","男",666.66,new Date());
User user2 = new User(2,"张三","男",666.66,new Date());
User user3 = new User(3,"张三","男",666.66,new Date());
User user4 = new User(4,"张三","男",666.66,new Date());
users.add(user1);
users.add(user2);
users.add(user3);
users.add(user4);
//向Excel表格中写数据
//创建ExcelWriter对象
ExcelWriter excelWriter = EasyExcel.write(fileName, User.class).build();
//创建sheet对象
WriteSheet writeSheet = EasyExcel.writerSheet("用户信息").build();
excelWriter.write(users,writeSheet);
//关闭ExcelWriter对象(一定要关闭,不然会导致内存溢出)
excelWriter.finish();
}
3.排除掉某些不想添加的字段
/**
* 排除掉模板中的某几项属性
*/
@Test
public void text03(){
//创建一个Excel文档
String fileName = "D:\\excel\\user3.xlsx";
//根据user模板构建数据
List<User> users = new ArrayList<>();
User user1 = new User(1,"张三","男",666.66,new Date());
User user2 = new User(2,"张三","男",666.66,new Date());
User user3 = new User(3,"张三","男",666.66,new Date());
User user4 = new User(4,"张三","男",666.66,new Date());
users.add(user1);
users.add(user2);
users.add(user3);
users.add(user4);
//设置排除的属性
Set<String> set = new HashSet<>();
set.add("salary");
set.add("hireDate");
//向Excel表格中写数据
EasyExcel.write(fileName, User.class).excludeColumnFiledNames(set).sheet("用户信息").doWrite(users);
}
4.复杂头数据写入
@ExcelProperty
注解的
value
属性是一个数组类型
,
设置多个
head
时会自动合并
/**
* 复杂头实体类
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class ComplexHeadUser {
@ExcelProperty(value = {"group1", "用户编号"}, index = 0)
private Integer userId;
@ExcelProperty(value = {"group1", "姓名"}, index = 1)
private String userName;
@ExcelProperty(value = {"group2", "入职时间"}, index = 2)
private Date hireDate;
}
/**
* 复杂头
*/
@Test
public void text05(){
//创建一个Excel文档
String fileName = "D:\\excel\\user5.xlsx";
//根据user模板构建数据
List<ComplexHeadUser> users = new ArrayList<>();
ComplexHeadUser user1 = new ComplexHeadUser(1,"张三",new Date());
ComplexHeadUser user2 = new ComplexHeadUser(2,"张三",new Date());
ComplexHeadUser user3 = new ComplexHeadUser(3,"张三",new Date());
users.add(user1);
users.add(user2);
users.add(user3);
//向Excel表格中写数据
EasyExcel.write(fileName, ComplexHeadUser.class).sheet("用户信息").doWrite(users);
}
//或
@Test
public void testWriteExcel6() {
String filename = "D:\\study\\user6.xlsx";
List<ComplexHeadUser> users = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
ComplexHeadUser user = ComplexHeadUser.builder()
.userId(i)
.userName("大哥" + i)
.hireDate(new Date())
.build();
users.add(user);
}
// 向Excel中写入数据
EasyExcel.write(filename, ComplexHeadUser.class)
.sheet("用户信息")
.doWrite(users);
}
处理百万级批量数据
处理百万级批量数据时,性能和内存管理是关键问题。
EasyExcel
提供了一些优化策略来处理大规模数据,包括分页读取和分批写入。下面是一些常用的优化技巧:
1.
分批写入
对于写操作,可以使用
EasyExcel
的分批写入功能,这样可以避免一次性加载大量数据导致的内存溢 出问题。
假设您有一个
User
类,定义如下:
/**
* 创建User类模板,通过User类模板向Excel表格中写数据
* @ExcelProperty这个注解用于指定Excel表格中该字段的显示名称(起名字)
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class User {
@ExcelProperty(value = "用户编号")
private Integer userId;
@ExcelProperty(value = "姓名")
private String userName;
@ExcelProperty(value = "性别")
private String gender;
@ExcelProperty(value = "工资")
private Double salary;
@ExcelProperty(value = "入职时间")
private Date hireDate;
// lombok 会生成getter/setter方法
}
/**
* 处理百万级批量数据
*/
@Test
public void text06(){
//创建一个Excel文档
String fileName = "D:\\excel\\user6.xlsx";
// 确保目录存在
File directory = new File("D:\\excel");
if (!directory.exists()) {
directory.mkdirs();
}
// 分批大小
int batchSize = 10000;
// 创建 ExcelWriter 对象
ExcelWriter excelWriter = EasyExcel.write(fileName, User.class).build();
for (List<User> data : dataList(batchSize)) {
// 创建 sheet 对象
WriteSheet writeSheet = EasyExcel.writerSheet("用户信息").build();
// 写数据
excelWriter.write(data, writeSheet);
}
excelWriter.finish();
System.out.println("Excel文件已成功创建!");
}
private List<List<User>> dataList(int batchSize) {
List<List<User>> allData = new ArrayList<>();
int totalRecords = 1000000; // 假设有 100 万条记录
for (int i = 0; i < totalRecords; i += batchSize) {
List<User> batch = new ArrayList<>();
for (int j = 0; j < batchSize && i + j < totalRecords; j++) {
User user = User.builder()
.userId(i + j + 1)
.userName("用户" + (i + j + 1))
.gender(j % 2 == 0 ? "男" : "女")
.salary(10000.0 + (i + j) * 1000.0)
.hireDate(new Date())
.build();
batch.add(user);
}
allData.add(batch);
}
return allData;
}
解释
1.
分批大小
:
batchSize
定义了每批写入的数据量。可以根据实际需求调整这个值。
2.
创建
ExcelWriter
对象
:使用
EasyExcel.write
方法创建
ExcelWriter
对象,并指定文件路径和数据模型类。
3.
分批写入数据
:
dataList
方法生成分批的数据列表,每次调用
doWrite
方法写入一批数据。