本文目录导读:

在Java中导入Excel文件,常用的方案主要有三种:Apache POI、EasyExcel(阿里开源的)和 JXL(已过时,不推荐)。
这里给你提供目前最主流的两种方案:Apache POI(功能全面,适合复杂处理)和 EasyExcel(性能优秀,适合大数据量,内存占用低)。
使用 Apache POI (最经典、最全面)
Apache POI 可以处理 .xls (HSSFWorkbook) 和 .xlsx (XSSFWorkbook) 格式。
添加 Maven 依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.5</version> <!-- 使用较新版本 -->
</dependency>
核心代码示例(读取 .xlsx 文件)
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ImportExcelByPOI {
public static List<List<String>> readExcel(String filePath) {
List<List<String>> result = new ArrayList<>();
try (InputStream is = new FileInputStream(filePath);
Workbook workbook = new XSSFWorkbook(is)) { // 支持 .xlsx
// 获取第一个工作表
Sheet sheet = workbook.getSheetAt(0);
if (sheet == null) {
return result;
}
// 遍历每一行
for (Row row : sheet) {
// 跳过空行
if (row == null) continue;
List<String> rowData = new ArrayList<>();
// 遍历单元格
for (Cell cell : row) {
// 根据单元格类型获取数据
String cellValue = getCellValue(cell);
rowData.add(cellValue);
}
result.add(rowData);
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
// 处理单元格数据类型的工具方法
private static String getCellValue(Cell cell) {
if (cell == null) return "";
return switch (cell.getCellType()) {
case STRING -> cell.getStringCellValue();
case NUMERIC -> {
// 如果是日期格式,处理为日期字符串,否则处理为数字
if (DateUtil.isCellDateFormatted(cell)) {
yield cell.getLocalDateTimeCellValue().toString(); // 或者自定义格式化
} else {
// 避免科学计数法,转为字符串
double value = cell.getNumericCellValue();
if (value == Math.floor(value) && !Double.isInfinite(value)) {
yield String.valueOf((long) value);
} else {
yield String.valueOf(value);
}
}
}
case BOOLEAN -> String.valueOf(cell.getBooleanCellValue());
case FORMULA -> cell.getCellFormula(); // 公式结果可以获取计算后的值
case BLANK -> "";
default -> "";
};
}
public static void main(String[] args) {
String filePath = "test.xlsx"; // 你的文件路径
List<List<String>> data = readExcel(filePath);
for (List<String> row : data) {
System.out.println(row);
}
}
}
POI 的优缺点:
- ✅ 优点:功能极其强大,能处理各种复杂的Excel格式(公式、样式、批注、图表等)。
- ❌ 缺点:内存消耗较大,如果一次性读取一个5万行的大文件,可能会触发
OutOfMemoryError。
使用 EasyExcel (推荐,高性能、低内存)
EasyExcel 是阿里巴巴开源的工具,对POI进行了封装,解决了大文件OOM问题,它是目前Java后端处理Excel的首选方案。
添加 Maven 依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>4.0.1</version> <!-- 使用较新版本 -->
</dependency>
核心代码示例(最简单的读取方式)
方式A:同步读取(小文件,一次性读进内存)
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import java.util.List;
public class ImportExcelByEasyExcel {
public static void main(String[] args) {
String fileName = "test.xlsx";
// 同步读取:数据量不大时直接用
List<Object> list = EasyExcel.read(fileName).sheet().doReadSync();
for (Object obj : list) {
// obj 默认是 List<String>,每一行的数据
System.out.println(obj);
}
}
}
方式B:异步读取(推荐,支持大文件,逐行处理)
需要定义一个监听器类 DemoDataListener,继承 ReadListener<DemoData>。
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
// 1. 定义数据模型(可选,但强烈推荐)
public class DemoData {
private String name;
private Integer age;
private String email;
// 必须要有 getter/setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
// 2. 定义监听器
public class DemoDataListener implements ReadListener<DemoData> {
private static final Logger log = LoggerFactory.getLogger(DemoDataListener.class);
private static final int BATCH_COUNT = 100; // 每100条批量处理一次
private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
private final DemoService demoService; // 可以注入业务Service
public DemoDataListener() {
this.demoService = new DemoService(); // 实际项目用 Spring 自动注入
}
@Override
public void invoke(DemoData data, AnalysisContext context) {
log.info("读取到一条数据:{}", data);
cachedDataList.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止内存溢出
if (cachedDataList.size() >= BATCH_COUNT) {
saveData(); // 批量处理
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); // 清空列表
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 处理最后一批不满BATCH_COUNT的数据
saveData();
log.info("所有数据解析完成!");
}
private void saveData() {
log.info("{}条数据,开始存储数据库!", cachedDataList.size());
demoService.saveBatch(cachedDataList); // 批量插入数据库
log.info("存储数据库成功!");
}
// 如果第一行是表头,可以用这个方法来获取表头信息
@Override
public void invokeHead(Map<Integer, ReadCellHead> headMap, AnalysisContext context) {
log.info("解析到表头:{}", headMap);
}
}
// 3. 使用
public static void main(String[] args) {
String fileName = "test.xlsx";
// 核心调用:只需一行
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
}
EasyExcel 的优缺点:
- ✅ 优点:
- 内存占用极低(使用行缓存,不会一次性将整个文件读入内存)。
- 使用简单,一行代码完成读取。
- 支持模型映射(注解
@ExcelProperty)。 - 性能好。
- ❌ 缺点:
对格式的处理不如POI细致(如样式、图表等),但日常数据导入场景完全够用。
总结与建议
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 日常数据导入 (几千行以内,简单格式) | EasyExcel | 代码简洁,内存占用低。 |
| 大数据量导入 (几万行以上) | EasyExcel (必须使用异步监听模式) | POI会OOM,EasyExcel是唯一选择。 |
| 需要处理复杂的Excel格式 (公式、图表、透视表、样式) | Apache POI | 功能最底层、最全面。 |
| 需要处理 .xls (2003旧格式) | POI 或 EasyExcel | 两者都支持,但POI支持更完善。 |
一句话总结:做Java后端导入Excel,首选 EasyExcel。