一、引入 easyexcel 依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcel.version}</version>
</dependency>
二、核心代码
public String readExcel(MultipartFile file) throws IOException {
// 创建 StringBuilder
StringBuilder stringBuilder = new StringBuilder("原始的excel数据\n");
// 获取文件字节数组
byte[] fileBytes = file.getBytes();
// 创建 Excel 读取器
// ByteInputSream(byte[] buf) -> 从字节数组创建一个内存输入流
// EasyExcel.raed(InputStream inputStream) -> ExcelReaderBuilder 对象
// .build() -> 来自 ExcelReaderBuilder类,返回值为 ExcelReader 对象
ExcelReader excelReader = EasyExcel.read(new ByteArrayInputStream(fileBytes)).build();
// .excelExecutor() -> 来自于 ExcelReader 执行器对象,获取执行器对象,用于执行各种操作, 返回 ExcelExecutor 对象
// .sheetList() -> 来自于 ExcelExecutor 类,读取 Excel 文件中所有工作表的元数据,放回值为 List<ReadSheet>
List<ReadSheet> readSheets = excelReader.excelExecutor().sheetList();
// 遍历每一个 sheet
for(ReadSheet readSheet : readSheets) {
// .getSheetNo() 方法 -> 来自 ReadSheet 类
// 获取当前工作表的序号(索引)
int sheetNo = readSheet.getSheetNo();
// 创建行数据容器
List<List<String>> sheetRows = new ArrayList<>();
// new ByteArrayInputStream(fileBytes) -> 再次创建内存输出流, 指定要处理的数据
// Map<Integer, String> 指定每行数据的类型,这次用 Map, 键是列索引(Integer), 值是单元格内容(String)
// new ReadListener<Map<Integer, String>>() {...} -> 匿名内部类写法, ReadListener是 EasyExcel 的读取监听器接口, 定义如何处理每一行数据(通过 ReadListener 监听器)
EasyExcel.read(new ByteArrayInputStream(fileBytes), new ReadListener<Map<Integer, String>>() {
// 重写 invoke 方法
// invoke 方法来自 ReadListener 接口 -> 每读取一行数据就会自动回调此方法
// 参数1data: 当前行的数据, Map<Integer, String> 类型
// 参数2analysisContext: 分析上下文
@Override
public void invoke(Map<Integer, String> data, AnalysisContext context) {
// 创建当前行的列数据容器
List<String> rowData = new ArrayList<>();
// 这行代码的作用是找出当前行最大的列列索引,如果没有任何列(空行)则返回-1
// data.KeySet() -> 来自 Map 接口,获取 Map 中所有键的集合, 返回值 Set<Integer>,所有列索引的集合
// .stream() -> 来自 Collection 接口,将集合转换为 Stream 流,用于函数式操作,返回值为 Stream<Integer> 流对象
// .mapToInt(Integer::intValue) -> 来自 Stream 接口,将 Stream 中的所有元素映射为 int 基本类型, 返回值为 IntStream 整数流
// .max() -> 来自 IntStream 接口,获取流中的最大值, 返回值为 OptionalInt, 可能包含最大值,也可能为空(如果流为空)
// .orElse(-1) -> 来时 OptionalInt 类, 如果 OptionalInt 有值则返回该值, 否则返回默认值
int maxColumnIndex = data.keySet().stream().mapToInt(Integer::intValue).max().orElse(-1);
for(int i = 0; i <= maxColumnIndex; i++) {
// value 为单元格值或空字符串
String value = data.getOrDefault(i, "");
// trim() -> 来自 String 类,去除字符串首尾的空白字符
rowData.add(value.trim());
}
sheetRows.add(rowData);
}
// 重写 doAfterAllAnalysed 方法
// doAfterAllAnalysed 方法,来自 ReadListener 接口, 将所有行读取完毕后自动回调次方法
// 参数 AnalysisContext 分析上下文
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
}
// .sheet(sheetNo) -> 来自 ExcelReaderBuilder 类,指定要读取的 sheet(按序号),返回值为 ExcelReaderBuilder自身
// .headRowNumber(0) -> 来自 ExcelReaderBuilder 类,设置表头函数, 参数0表示不跳过任何行,从第一行开始读取,ExcelReaderBuilder(自身)
// .doRead() -> 来自 ExcelReaderBUilder 类,真正开始执行读取操作, 会触发 invoke 方法逐行回调
}).sheet(sheetNo).headRowNumber(0).doRead();
// 拼接 Sheet 标题
stringBuilder.append("\n==sheet: ").append(readSheet.getSheetName())
.append("(第").append(sheetNo + 1).append("个工作表)==\n");
for(List<String> row : sheetRows) {
for(int i = 0; i < row.size(); i++) {
if(i > 0) {
stringBuilder.append("|");
}
stringBuilder.append(row.get(i));
}
stringBuilder.append("\n");
}
}
return stringBuilder.toString();
}
三、测试
package com.linzhixin.test;
import com.linzhixin.service.ChatService;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
public class ChatServiceTest {
private ChatService chatService = new ChatService();
@Test
public void testReadExcel() throws IOException {
String path = "D:\\浏览器下载\\chat2excel-master\\excel数据\\国家GDP.xlsx";
// 创建 File 对象
File excelFile = new File(path);
// 读取文件为字节数组
byte[] excelBytes = Files.readAllBytes(excelFile.toPath());
// 创建 MultipartFile 对象
// MockMultipartFile -> Spring 测试工具类,用于模拟文件上传对象
// 第一个参数:excelFile.getName -> 文件名参数表
// 第二个参数:excelFile.getName -> 原始文件名
// 第三个参数:application/vnd.openxmlformats-officedocument.spreadshhettml.sheet" -> 文件 MIME 类型
// 第四个参数:excelBytes -> 文件字节内容
MultipartFile file = new MockMultipartFile(
excelFile.getName(),
excelFile.getName(),
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
excelBytes
);
//执行并打印结果
System.out.println(chatService.readExcel(file));
}
}
五、参考文档链接
读Excel | Easy Excel 官网