pom.xml
java
<!-- 引入EasyExcel依赖,用于处理Excel读写操作 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.1</version>
</dependency>
<!-- 引入FastJSON依赖,用于处理JSON格式数据的序列化和反序列化 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.21</version>
</dependency>
准备一个Excel文件
字符串标题 日期标题 数字标题
字符串0 2024/5/29 21:16 0.56
字符串1 2024/5/30 21:16 1.56
字符串2 2024/5/31 21:16 2.56
字符串3 2024/6/1 21:16 3.56
字符串4 2024/6/2 21:16 4.56
字符串5 2024/6/3 21:16 5.56
字符串6 2024/6/4 21:16 6.56
字符串7 2024/6/5 21:16 7.56
字符串8 2024/6/6 21:16 8.56
实体类
java
@Data
@HeadRowHeight(20)
@ColumnWidth(20)
public class DemoData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
@ExcelIgnore
private String ignore;
}
读文件
读文件的两种方法
JavaExcelApplicationTests
java
/**
* 读取Excel文件并将其内容转换为JSON格式的日志信息。
* 该方法不接受参数且无返回值。
* 主要步骤包括定位并读取Excel文件,将读取到的数据转换为DemoData对象的列表,
* 然后将每个DemoData对象转换为JSON字符串,最后通过日志记录这些JSON字符串。
*
* {"date":"2024-05-29 21:16:57","doubleData":0.56,"string":"字符串0"}
* {"date":"2024-05-30 21:16:57","doubleData":1.56,"string":"字符串1"}
*
*/
@Test
void readExcel() {
// 读取指定路径的Excel文件,指定表头类型为DemoData,读取所有sheet,同步读取数据
List<DemoData> list = EasyExcel.read("D:\\Users\\黑池\\Desktop\\test.xlsx").head(DemoData.class).sheet().doReadSync();
// 遍历读取到的数据列表,将每个DemoData对象转换为JSON字符串,并通过日志输出
list.forEach(data ->{
String string = JSON.toJSONString(data);
log.info(string);
});
}
/**
* 读取Excel文件内容并打印
*
* 该方法没有参数。
* 该方法没有返回值,但会读取指定路径的Excel文件,将每一行的内容转换为Map形式,并打印这些Map对象的信息。
*
* {0=字符串0, 1=2024-5-29 21:16, 2=0.56}
* {0=字符串1, 1=2024-5-30 21:16, 2=1.56}
*/
@Test
void readExcel2() {
// 读取Excel文件内容,同步执行
List<Map<Integer,Object>> list = EasyExcel.read("D:\\Users\\黑池\\Desktop\\test.xlsx").sheet().doReadSync();
// 遍历并打印读取到的每一行数据
list.forEach(data ->{
log.info(data.toString());
});
}
使用监听器读文件
java
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson2.JSON;
import com.baize.entity.DemoData;
import com.baize.service.DemoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
@RequiredArgsConstructor
@Slf4j
public class DemoDataListener extends AnalysisEventListener<DemoData> {
// 每次处理的数据批量大小
private static final int BATCH_COUNT = 100;
// 用于存储DemoData对象的列表,预先分配大小以提高性能
private List<DemoData> list = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
// DemoService的实例,用于执行与DemoData相关的业务逻辑
private final DemoService demoService;
// @RequiredArgsConstructor
// public DemoDataListener(DemoService demoService) {
// this.demoService = demoService;
// }
// @Override
// public void doAfterALlAnalysed(AnalysisContext context){
// //这里也要保存数据,确保最后遗留的数据也存储到数据库
// saveData();
// log.info("所有数据解析完成!");
// }
/**
* 保存数据到数据库。
* 该方法不接受参数并且没有返回值。
* 主要逻辑包括:
* 1. 打印日志,记录待存储数据的数量。
* 2. 调用demoService的saveData方法,将数据列表存储到数据库。
* 3. 打印日志,表示数据存储成功。
*/
private void saveData(){
// 记录开始存储数据时的数据量
log.info("{}条数据,开始存储数据库!",list.size());
demoService.saveData(list); // 将数据列表保存到数据库
// 记录数据保存成功
log.info("存储数据库成功!");
}
/**
* 被调用以处理DemoData数据,并将其存储到数据库中。
* 当积累到一定数量的数据时,会批量存储到数据库中,以减少内存占用。
*
* @param demoData 待处理的数据对象,包含需要分析的数据。
* @param analysisContext 分析上下文,提供额外的环境信息或配置。
*/
@Override
public void invoke(DemoData demoData, AnalysisContext analysisContext) {
log.info("解析到一条数据:{}", JSON.toJSON(demoData));
list.add(demoData);
// 检查数据积累是否达到批量存储的阈值
if (list.size() >= BATCH_COUNT) {
saveData(); // 执行数据库存储操作
// 存储完成后清理内存,准备接收新的数据批次
list = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
//这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
log.info("所有数据解析完成!");
}
}
service文件
java
public interface DemoService {
void saveData(List<DemoData> list);
}
serviceimpl文件
java
@Slf4j
@Service
public class DemoServiceimpl implements DemoService {
public void saveData(){
log.info("保存Excel数据到数据库中");
}
@Override
public void saveData(List<DemoData> list) {
log.info("正在保存Excel数据到数据库中");
}
}
JavaExcelApplicationTests
java
/**
* 读取Excel文件的示例方法。
* <p>
* 此方法使用EasyExcel框架读取指定路径下的Excel文件("D:\\Users\\黑池\\Desktop\\test.xlsx"),
* 并将数据映射到DemoData类中,然后通过DemoDataListener将数据进一步处理(这里依赖于demoService)。
* <p>
* 方法不接受任何参数,并且没有返回值。
*
* 解析到一条数据:{"date":"2024-05-29 21:16:57","doubleData":0.56,"string":"字符串0"}
* 解析到一条数据:{"date":"2024-05-30 21:16:57","doubleData":1.56,"string":"字符串1"}
* ......
* 解析到一条数据:{"date":"2024-06-06 21:16:57","doubleData":8.56,"string":"字符串8"}
* 9条数据,开始存储数据库!
* 存储数据库成功!
* 所有数据解析完成!
*/
@Test
void readExcel3() {
// 使用EasyExcel框架读取Excel文件并处理数据
EasyExcel.read("D:\\Users\\黑池\\Desktop\\test.xlsx", DemoData.class, new DemoDataListener(demoService)).sheet().doReadSync();
}
写入文件
excel的写入
java
//模拟20条数据
private List<DemoData> data() {
List<DemoData> list = new ArrayList<DemoData>();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字符串" + i);
data.setDate(new Date());
data.setDoubleData(0.56);
list.add(data);
}
return list;
}
/**
* 使用EasyExcel框架写入Excel文件的示例方法1。
* 这个方法展示了两种不同的方式来写入Excel文件,均不需要手动关闭文件流。
* 方法会根据调用方式的不同,演示了两种写入数据的方式。
*
* @since 3.0.0-beta1
*/
@Test
void writeExcel1(){
// 写法1 JDK8+
// 通过lambda表达式动态提供数据
String fileName = "EasyExcelDemo" + System.currentTimeMillis() + ".xlsx";
// 使用指定的类模型写入数据到第一个sheet,sheet名称为"模板"
EasyExcel.write(fileName, DemoData.class)
.sheet("模板")
.doWrite(() -> {
// 动态查询或生成数据
return data();
});
// 写法2
// 直接传入数据源
fileName = "EasyExcelDemo" + System.currentTimeMillis() + ".xlsx";
// 同上,但是直接传入数据列表,而不是通过lambda表达式
EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
}
上传下载
UploadDownController
java
package com.baize.controller;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.baize.config.DemoDataListener;
import com.baize.entity.DemoData;
import com.baize.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
@Controller
public class UploadDownController {
@Autowired
private DemoService demoService;
/**
* 提供文件下载功能。特别注意,使用Swagger可能导致各种问题,建议使用浏览器或Postman进行测试。
*
* @param response 用于配置响应的HttpServletResponse对象,通过它将文件提供给客户端下载。
* @throws IOException 如果在写入响应流时发生IO错误。
*/
@GetMapping("download")
public void download(HttpServletResponse response) throws IOException {
// 设置响应内容类型为Excel文件
response.setContentType("application/vnd.ms-excel");
// 设置响应字符编码为UTF-8
response.setCharacterEncoding("utf-8");
// 对文件名进行URL编码,防止中文或其他特殊字符导致的乱码问题
String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
// 设置响应头,指定文件下载的名称和方式
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
// 使用EasyExcel框架将数据写入响应输出流,以提供文件下载
EasyExcel.write(response.getOutputStream(), DemoData.class).sheet("模板").doWrite(data());
}
/**
* 文件下载接口。尝试下载一个Excel文件,如果失败,则返回一个包含错误信息的JSON。
* 默认情况下,当下载失败时,会返回一个部分数据的Excel文件。
*
* @param response 用于响应客户端请求的HttpServletResponse对象。
* @throws IOException 如果发生I/O错误。
* @since 2.1.1
*/
@GetMapping("api/download")
public void downloadApi(HttpServletResponse response) throws IOException {
try {
// 设置响应类型为Excel文件,配置响应头以支持中文文件名
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
// 使用EasyExcel框架写入Excel文件到响应输出流,不关闭流以保持连接
EasyExcel.write(response.getOutputStream(), DemoData.class).autoCloseStream(Boolean.FALSE).sheet("模板")
.doWrite(data());
} catch (Exception e) {
// 下载失败时的处理:重置响应,返回JSON错误信息
response.reset();
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
Map<String, String> map = new HashMap<String, String>();
map.put("code", "500");
map.put("message", "下载文件失败" + e.getMessage());
response.getWriter().println(JSON.toJSONString(map));
}
}
/**
* 文件上传接口。
* 该方法接收一个文件,使用EasyExcel框架读取并处理文件内容。
*
* @param file 用户上传的文件,类型为MultipartFile。
* @return 返回一个字符串表示上传结果,成功则返回"success"。
* @throws IOException 如果读取文件发生错误,则抛出IOException。
*/
@PostMapping("upload")
@ResponseBody
public String upload(MultipartFile file) throws IOException {
// 使用EasyExcel框架读取上传文件的内容,并将其转换为DemoData对象,然后通过DemoDataListener处理这些对象
EasyExcel.read(file.getInputStream(), DemoData.class, new DemoDataListener(demoService)).sheet().doRead();
return "success";
}
/**
* 生成包含10个DemoData对象的列表。
* <p>此方法不接受任何参数。</p>
*
* @return 返回一个包含10个初始化的DemoData对象的列表。
*/
private List<DemoData> data() {
// 创建一个空的DemoData对象列表
List<DemoData> list = new ArrayList<>();
// 循环创建10个DemoData对象并填充数据,然后添加到列表中
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
// 为每个DemoData对象设置字符串属性
data.setString("字符串" + 0);
// 为每个DemoData对象设置当前日期
data.setDate(new Date());
// 为每个DemoData对象设置双精度浮点数属性
data.setDoubleData(0.56);
list.add(data);
}
return list;
}
}
可以写工具类
java
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;
import lombok.extern.slf4j.Slf4j;
import java.io.InputStream;
import java.util.List;
@Slf4j
public class ExcelUtils {
/**
* 从Excel文件中读取数据并将其转换为指定模型的列表。
*
* @param inputStream Excel文件的输入流。
* @param clazz 指定的模型类,用于将Excel单元格数据转换为该类型的对象。
* @return 一个包含读取到的数据的列表,每个元素都是clazz指定的类型。
* @throws NullPointerException 如果输入流为null,则抛出此异常。
*/
public static <T> List<T> getExcelModelData(final InputStream inputStream, Class<T> clazz) {
// 检查输入流是否为null
if (null == inputStream) {
throw new NullPointerException("the inputStream is null!");
}
// 使用EasyExcel框架初始化Excel读取构建器
ExcelReaderBuilder result = EasyExcel.read(inputStream, clazz, null);
// 获取默认工作表的构建器
ExcelReaderSheetBuilder sheet1 = result.sheet();
// 同步读取工作表数据并返回
List<T> resultData = sheet1.doReadSync();
return resultData;
}
}