Java解析、生成Excel比较有名的框架有Apache poi、jxl等,使用者可自行斟酌。

一、 为什么使用 EasyExcel

1.1 内存控制

Apache poi、jxl也能解析Excel,但他们都存在一个问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。

easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便。

1.2 使用简洁

EasyExcel 可以映射excel和实体类,让代码变的更加简洁,读写更方便。

二、springboot 整合 EasyExcel

2.1 添加maven依赖

org.springframework.boot

spring-boot-starter-web

com.alibaba

easyexcel

3.3.3

com.alibaba

fastjson

org.projectlombok

lombok

1.18.24

2.2 读Excel

2.2.1 准备数据源,Excel 表

姓名证件号证件类型张三500000000000000000x户口簿李四5111111111111111111身份证王不二5222222222222222222身份证赵五5252513xdg护照

2.2.2 创建与数据表对应的实体类

import com.alibaba.excel.annotation.ExcelProperty;

import com.alibaba.excel.annotation.format.DateTimeFormat;

import com.alibaba.excel.annotation.format.NumberFormat;

import lombok.Data;

import lombok.EqualsAndHashCode;

/**

* @author lqf

* @date 2024/1/11 11:00

*/

@Data

@EqualsAndHashCode

public class AccountCenterByExcelVO {

/**

* 姓名

*/

// 可以使用指定 表格下标或者 表头名字 的方式来对应数据。视情况选择,优弊如下:

// 使用表下标,会让制表变得不灵活,使用表名会造成 名字重复,只有一个字段读取到数据

// @ExcelProperty(index = 1)

@ExcelProperty("姓名")

private String name;

/**

* 证件号

*/

@ExcelProperty("证件号")

private String IdNumber;

/**

* 证件类型

*/

@ExcelProperty("证件类型")

private String IdType;

/**

* 这里用string 去接日期才能格式化。我想接收年月日格式

*/

@ExcelProperty("日期")

@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")

private String date;

/**

* 想接收百分比的数字

*/

@ExcelProperty("余额")

@NumberFormat("#.##%")

private String doubleData;

}

2.2.3 自定义转换器(可以不自定义,可使用EasyExcel 自带的 PageReadListener)

import com.alibaba.excel.read.listener.ReadListener;

import com.alibaba.excel.context.AnalysisContext;

import com.alibaba.excel.util.ListUtils;

import com.alibaba.fastjson.JSON;

import com.insupro.flexibleServerJ.dto.resp.vo.AccountCenterByExcelVO;

import lombok.extern.slf4j.Slf4j;

import java.util.List;

/**

* 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去

* @author lqf

*/

@Slf4j

public class ACByExcelListener implements ReadListener {

/**

* 每隔 N 条 进行一次数据处理,根据实际使用情况处理,然后清理list ,方便内存回收

*/

private static final int BATCH_COUNT = 100;

/**

* 缓存的数据

*/

private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

/**

* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。

* 用来存储或者处理数据

*/

private AccountCenterByExcelVO excelVo;

/**

* 每次创建Listener的时候需要把spring管理的类传进来

*

* @param excelVo

*/

public ACByExcelListener(AccountCenterByExcelVO excelVo) {

this.excelVo = excelVo;

}

/**

* 这个每一条数据解析都会来调用

*

* @param data

* @param context

*/

@Override

public void invoke(AccountCenterByExcelVO data, AnalysisContext context) {

cachedDataList.add(data);

// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM

if (cachedDataList.size() >= BATCH_COUNT) {

saveData();

// 存储完成清理 list

cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

}

}

/**

* 所有数据解析完成了 都会来调用

*

* @param context

*/

@Override

public void doAfterAllAnalysed(AnalysisContext context) {

// 这里也要保存数据,确保最后遗留的数据也存储到数据库

saveData();

log.info("所有数据解析完成!");

}

/**

* 加上存储数据库

*/

public void saveData() {

log.info("{}条数据,开始处理数据!", cachedDataList.size());

log.info("解析结果:"+JSON.toJSONString(cachedDataList));

// 调用业务逻辑服务处理数据,实例是假装有业务逻辑

log.info(excelVo.toString());

log.info("数据处理成功!");

}

}

2.2.4 创建一个文件处理 Controller

只是示例代码,业务逻辑没有写入server 层,使用的时候自行将业务代码归类

import com.alibaba.excel.EasyExcel;

import com.alibaba.excel.read.listener.PageReadListener;

import com.alibaba.fastjson.JSON;

import com.insupro.flexibleServerJ.dto.resp.vo.AccountCenterByExcelVO;

import com.insupro.flexibleServerJ.utils.ACByExcelListener;

import lombok.extern.slf4j.Slf4j;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;

import java.util.List;

/**

* @author lqf

* @date 2024/1/11 13:46

*/

@Slf4j

@RestController

@RequestMapping("api/easyExcel")

public class EasyExcelController {

@RequestMapping("upExcel")

public String upExcel(MultipartFile multipartFile){

try {

List acvoList = new ArrayList<>();

// 第一种 使用 EasyExcel 自带的 PageReadListener,可以将数据读取后拿到读取的所有数据

// 这里默认每次会读取100条数据 然后返回过来 直接调用使用数据就行

// 具体需要返回多少行可以在`PageReadListener`的构造函数设置

EasyExcel.read(multipartFile.getInputStream(), AccountCenterByExcelVO.class,

new PageReadListener(dateList ->{

// 这里默认每次会读取100条数据,然后进入下一次读取

log.info(JSON.toJSONString(dateList));

// 保存每一次的数据读取即可

acvoList.addAll(dateList);

}))

.sheet()

.doRead();

System.out.println(acvoList);

// 第二种,使用自定义监听器, 数据集处理需要再监听器中定义

EasyExcel.read(multipartFile.getInputStream(), AccountCenterByExcelVO.class,

new ACByExcelListener(new AccountCenterByExcelVO()))

.sheet()

// 不添加属性,默认头部是1行

.headRowNumber(1)

.doRead();

}catch (Exception e){

e.printStackTrace();

}

return "完成";

}

}

2.3 写出excel

2.3.1 创建写出的 实体类

import com.alibaba.excel.annotation.ExcelProperty;

import com.alibaba.excel.annotation.format.DateTimeFormat;

import com.alibaba.excel.annotation.format.NumberFormat;

import lombok.Data;

import lombok.EqualsAndHashCode;

import java.util.Date;

/**

* @author lqf

* @date 2024/1/11 11:00

*/

@Data

@EqualsAndHashCode

public class AccountWriteForExcelVO {

/**

* 姓名

* */

// 可以使用指定 表格下标或者 表头名字 的方式来对应数据。视情况选择,优弊如下:

// 使用表下标,会让制表变得不灵活,使用表名会造成 名字重复,只有一个字段读取到数据

// @ExcelProperty(index = 1)

@ExcelProperty("姓名")

private String name;

/**

* 证件号

*/

@ExcelProperty("证件号")

private String IdNumber;

/**

* 证件类型

*/

@ExcelProperty("证件类型")

private String IdType;

/**

* 这里用string 去接日期才能格式化。我想接收年月日格式

*/

@ExcelProperty("日期")

@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")

private Date date;

/**

* 想接收百分比的数字

*/

@ExcelProperty("余额")

@NumberFormat("#.##%")

private Double doubleData;

}

2.3.2 实现简单写出逻辑

import com.alibaba.excel.EasyExcel;

import com.alibaba.excel.ExcelWriter;

import com.alibaba.excel.util.ListUtils;

import com.alibaba.excel.write.metadata.WriteSheet;

import vo.AccountWriteForExcelVO;

import java.util.Date;

import java.util.List;

/**

* @author lqf

* @date 2024/1/11 11:00

*/

public class WriteExcleDemo {

public static void main(String[] args) {

// 使用自己指定输出的文件路径

String fileName = "D:/workSpace/" + "simpleWrite" + System.currentTimeMillis() + ".xlsx";

System.out.println(fileName);

// 注意 数据量大参照 重复多次写入

// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭

// 如果这里想使用03 则 传入excelType参数即可

EasyExcel.write(fileName, AccountWriteForExcelVO.class)

.sheet("模板")

.doWrite(() -> {

// 分页查询数据

return data();

});

// 写法2

// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭

// 如果这里想使用03 则 传入excelType参数即可

EasyExcel.write(fileName, AccountWriteForExcelVO.class).sheet("模板").doWrite(data());

// 写法3

// 这里 需要指定写用哪个class去写

try (ExcelWriter excelWriter = EasyExcel.write(fileName, AccountWriteForExcelVO.class).build()) {

WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();

excelWriter.write(data(), writeSheet);

}

}

private static List data() {

List list = ListUtils.newArrayList();

for (int i = 0; i < 10; i++) {

AccountWriteForExcelVO data = new AccountWriteForExcelVO();

data.setName("字符串" + i);

data.setDate(new Date());

data.setDoubleData(0.56);

list.add(data);

}

return list;

}

}

结语

更多使用方法,见官方文档 https://easyexcel.opensource.alibaba.com/docs/current/

推荐链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: