Java使用Apache POI和EasyExcel讀取Excel文件的實(shí)現(xiàn)方案
Java 讀取 Excel 文件核心依賴 Apache POI(兼容 .xls(Excel 97-2003)和 .xlsx(Excel 2007+))或 EasyExcel(阿里開源,低內(nèi)存、高性能),以下是兩種主流方案的完整實(shí)現(xiàn),覆蓋「讀取簡(jiǎn)單單元格、讀取指定sheet、讀取表頭+數(shù)據(jù)」等場(chǎng)景。
一、前置準(zhǔn)備:引入依賴
方案 1:Apache POI(功能全,兼容所有Excel版本)
Maven 依賴(需同時(shí)引入 poi 和 poi-ooxml,分別對(duì)應(yīng) .xls 和 .xlsx):
<dependencies>
<!-- 核心依賴:處理 .xls -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.5</version> <!-- 推薦最新穩(wěn)定版 -->
</dependency>
<!-- 處理 .xlsx -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.5</version>
</dependency>
<!-- 可選:簡(jiǎn)化日期格式處理 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>4.1.2</version>
</dependency>
</dependencies>
方案 2:EasyExcel(阿里開源,低內(nèi)存,推薦大數(shù)據(jù)量)
Maven 依賴(僅需核心包,自動(dòng)兼容 .xls/.xlsx):
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version> <!-- 最新版 -->
</dependency>
<!-- 可選:日志依賴(EasyExcel 依賴 slf4j) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.9</version>
</dependency>
二、方案 1:Apache POI 讀取 Excel(通用場(chǎng)景)
場(chǎng)景 1:讀取所有sheet的所有單元格(基礎(chǔ)版)
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import java.io.FileInputStream;
import java.io.IOException;
public class POIExcelReader {
public static void main(String[] args) {
String filePath = "D:/test.xlsx"; // 替換為你的Excel文件路徑
// 區(qū)分 .xls 和 .xlsx
Workbook workbook = null;
try (FileInputStream fis = new FileInputStream(filePath)) {
if (filePath.endsWith(".xlsx")) {
workbook = new XSSFWorkbook(fis); // .xlsx
} else if (filePath.endsWith(".xls")) {
workbook = new HSSFWorkbook(fis); // .xls
} else {
throw new IllegalArgumentException("不支持的Excel格式");
}
// 遍歷所有sheet
for (Sheet sheet : workbook) {
System.out.println("===== Sheet名稱:" + sheet.getSheetName() + " =====");
// 遍歷所有行(跳過表頭:從第1行開始,rowNum=1)
for (Row row : sheet) {
// 遍歷該行所有單元格
for (Cell cell : row) {
// 獲取單元格值(統(tǒng)一格式)
String cellValue = getCellValue(cell);
System.out.print(cellValue + "\t");
}
System.out.println(); // 換行
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (workbook != null) {
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 工具方法:統(tǒng)一處理不同類型的單元格值
private static String getCellValue(Cell cell) {
if (cell == null) {
return "";
}
switch (cell.getCellType()) {
case STRING: // 字符串
return cell.getStringCellValue();
case NUMERIC: // 數(shù)字/日期
if (DateUtil.isCellDateFormatted(cell)) {
// 日期類型
return cell.getDateCellValue().toString();
} else {
// 數(shù)字類型(避免科學(xué)計(jì)數(shù)法)
return String.valueOf(cell.getNumericCellValue());
}
case BOOLEAN: // 布爾值
return String.valueOf(cell.getBooleanCellValue());
case FORMULA: // 公式
return cell.getCellFormula() + " = " + cell.getCachedFormulaResultType();
case BLANK: // 空單元格
return "";
default:
return "";
}
}
}
場(chǎng)景 2:讀取指定sheet和指定行(精準(zhǔn)讀?。?/h3>
// 讀取指定sheet(索引從0開始,或按名稱)
Sheet sheet = workbook.getSheetAt(0); // 第一個(gè)sheet
// 或 Sheet sheet = workbook.getSheet("用戶數(shù)據(jù)"); // 按名稱
// 讀取指定行(如第2行,rowNum=1)
Row targetRow = sheet.getRow(1);
if (targetRow != null) {
// 讀取指定單元格(如第3列,cellNum=2)
Cell targetCell = targetRow.getCell(2);
String value = getCellValue(targetCell);
System.out.println("指定單元格值:" + value);
}
// 遍歷有效行(跳過空行)
int lastRowNum = sheet.getLastRowNum(); // 最后一行索引
for (int rowNum = 1; rowNum <= lastRowNum; rowNum++) {
Row row = sheet.getRow(rowNum);
if (row == null) {
continue; // 跳過空行
}
// 讀取該行單元格
String name = getCellValue(row.getCell(0)); // 第1列:姓名
String age = getCellValue(row.getCell(1)); // 第2列:年齡
System.out.println("姓名:" + name + ",年齡:" + age);
}
// 讀取指定sheet(索引從0開始,或按名稱)
Sheet sheet = workbook.getSheetAt(0); // 第一個(gè)sheet
// 或 Sheet sheet = workbook.getSheet("用戶數(shù)據(jù)"); // 按名稱
// 讀取指定行(如第2行,rowNum=1)
Row targetRow = sheet.getRow(1);
if (targetRow != null) {
// 讀取指定單元格(如第3列,cellNum=2)
Cell targetCell = targetRow.getCell(2);
String value = getCellValue(targetCell);
System.out.println("指定單元格值:" + value);
}
// 遍歷有效行(跳過空行)
int lastRowNum = sheet.getLastRowNum(); // 最后一行索引
for (int rowNum = 1; rowNum <= lastRowNum; rowNum++) {
Row row = sheet.getRow(rowNum);
if (row == null) {
continue; // 跳過空行
}
// 讀取該行單元格
String name = getCellValue(row.getCell(0)); // 第1列:姓名
String age = getCellValue(row.getCell(1)); // 第2列:年齡
System.out.println("姓名:" + name + ",年齡:" + age);
}
三、方案 2:EasyExcel 讀取 Excel(高性能,推薦大數(shù)據(jù)量)
EasyExcel 無需加載整個(gè)Excel到內(nèi)存,適合讀取十萬級(jí)以上數(shù)據(jù),核心是通過「監(jiān)聽器」逐行讀取。
步驟 1:定義數(shù)據(jù)實(shí)體(與Excel表頭映射)
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
// 對(duì)應(yīng)Excel的表頭:姓名、年齡、手機(jī)號(hào)
@Data // Lombok注解,自動(dòng)生成get/set
public class UserExcelDTO {
// value:Excel表頭名稱,index:列索引(可選)
@ExcelProperty(value = "姓名", index = 0)
private String name;
@ExcelProperty(value = "年齡", index = 1)
private Integer age;
@ExcelProperty(value = "手機(jī)號(hào)", index = 2)
private String phone;
}
步驟 2:自定義監(jiān)聽器(處理讀取到的數(shù)據(jù))
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import java.util.ArrayList;
import java.util.List;
// 自定義監(jiān)聽器,逐行讀取數(shù)據(jù)并存儲(chǔ)
public class UserExcelListener extends AnalysisEventListener<UserExcelDTO> {
// 存儲(chǔ)讀取到的數(shù)據(jù)
private List<UserExcelDTO> dataList = new ArrayList<>();
// 每讀取一行數(shù)據(jù)觸發(fā)
@Override
public void invoke(UserExcelDTO user, AnalysisContext context) {
dataList.add(user);
System.out.println("讀取到數(shù)據(jù):" + user);
// 可在此處批量處理(如每1000條插入數(shù)據(jù)庫)
if (dataList.size() >= 1000) {
handleData(); // 處理數(shù)據(jù)
dataList.clear(); // 清空
}
}
// 所有數(shù)據(jù)讀取完成后觸發(fā)
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
handleData(); // 處理剩余數(shù)據(jù)
System.out.println("Excel讀取完成,總數(shù)據(jù)量:" + dataList.size());
}
// 數(shù)據(jù)處理邏輯(如插入數(shù)據(jù)庫)
private void handleData() {
if (!dataList.isEmpty()) {
// TODO: 批量插入數(shù)據(jù)庫/業(yè)務(wù)處理
System.out.println("批量處理" + dataList.size() + "條數(shù)據(jù)");
}
}
// 獲取讀取到的所有數(shù)據(jù)
public List<UserExcelDTO> getDataList() {
return dataList;
}
}
步驟 3:讀取Excel文件(核心代碼)
import com.alibaba.excel.EasyExcel;
import java.util.List;
public class EasyExcelReader {
public static void main(String[] args) {
String filePath = "D:/test.xlsx";
// 初始化監(jiān)聽器
UserExcelListener listener = new UserExcelListener();
// 讀取Excel(指定文件路徑、實(shí)體類、監(jiān)聽器)
EasyExcel.read(filePath, UserExcelDTO.class, listener)
.sheet("用戶數(shù)據(jù)") // 指定sheet名稱(可選,默認(rèn)第一個(gè))
.headRowNumber(1) // 表頭行數(shù)(默認(rèn)1行)
.doRead(); // 執(zhí)行讀取
// 獲取所有數(shù)據(jù)
List<UserExcelDTO> dataList = listener.getDataList();
System.out.println("最終讀取到的數(shù)據(jù):" + dataList);
}
}
四、關(guān)鍵注意事項(xiàng)
1. 文件路徑與權(quán)限
- 確保文件路徑無中文/空格(避免
FileNotFoundException); - 若讀取服務(wù)器文件,需保證Java進(jìn)程有文件讀取權(quán)限(如Linux下
chmod 755)。
2. 版本兼容
.xls(HSSFWorkbook)最大支持65536行,.xlsx(XSSFWorkbook)無行數(shù)限制;- EasyExcel 自動(dòng)兼容兩種格式,無需手動(dòng)區(qū)分。
3. 性能優(yōu)化
- Apache POI 讀取大數(shù)據(jù)量Excel易內(nèi)存溢出,需用
SXSSFWorkbook(流式讀?。?; - EasyExcel 天生適合大數(shù)據(jù)量,無需額外配置。
4. 日期/數(shù)字格式
- Apache POI 需手動(dòng)判斷日期格式(
DateUtil.isCellDateFormatted); - EasyExcel 可通過
@ExcelProperty(converter = DateConverter.class)自定義格式轉(zhuǎn)換。
五、兩種方案對(duì)比
| 方案 | 優(yōu)點(diǎn) | 缺點(diǎn) | 適用場(chǎng)景 |
|---|---|---|---|
| Apache POI | 功能全、兼容所有Excel特性 | 大數(shù)據(jù)量易內(nèi)存溢出、代碼繁瑣 | 小數(shù)據(jù)量、需操作復(fù)雜Excel(公式/宏) |
| EasyExcel | 低內(nèi)存、代碼簡(jiǎn)潔、高性能 | 不支持宏/復(fù)雜公式 | 大數(shù)據(jù)量、普通數(shù)據(jù)讀?。ㄍ扑]) |
六、常見問題解決
1.FileNotFoundException
- 檢查文件路徑是否正確(絕對(duì)路徑/相對(duì)路徑);
- 檢查文件是否被占用(如Excel未關(guān)閉)。
2. 內(nèi)存溢出(OOM)
- Apache POI:改用
SXSSFWorkbook流式讀??; - 優(yōu)先使用 EasyExcel。
3. 日期讀取為數(shù)字
- Apache POI:通過
DateUtil.isCellDateFormatted判斷并轉(zhuǎn)換; - EasyExcel:配置日期轉(zhuǎn)換器。
核心原則:小數(shù)據(jù)量/復(fù)雜Excel用 Apache POI,大數(shù)據(jù)量/普通讀取用 EasyExcel;讀取時(shí)務(wù)必處理空單元格和格式轉(zhuǎn)換,避免空指針/格式錯(cuò)誤。
以上就是Java使用Apache POI和EasyExcel讀取Excel文件的實(shí)現(xiàn)方案的詳細(xì)內(nèi)容,更多關(guān)于Java Apache POI和EasyExcel讀取Excel的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringAop @Aspect織入不生效,不執(zhí)行前置增強(qiáng)織入@Before方式
這篇文章主要介紹了SpringAop @Aspect織入不生效,不執(zhí)行前置增強(qiáng)織入@Before方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
SpringBoot2 整合MinIO中間件實(shí)現(xiàn)文件便捷管理功能
這篇文章主要介紹了SpringBoot2 整合MinIO中間件,實(shí)現(xiàn)文件便捷管理,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07
Java實(shí)現(xiàn)簡(jiǎn)易生產(chǎn)者消費(fèi)者模型過程解析
這篇文章主要介紹了Java實(shí)現(xiàn)簡(jiǎn)易生產(chǎn)者消費(fèi)者模型過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
Java線程并發(fā)工具類CountDownLatch原理及用法
這篇文章主要介紹了Java線程并發(fā)工具類CountDownLatch原理及用法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
Java數(shù)據(jù)結(jié)構(gòu)及算法實(shí)例:插入排序 Insertion Sort
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)及算法實(shí)例:插入排序 Insertion Sort,本文直接給出實(shí)例代碼,代碼中包含詳細(xì)注釋,需要的朋友可以參考下2015-06-06
JPA?@ManyToMany?報(bào)錯(cuò)StackOverflowError的解決
這篇文章主要介紹了JPA?@ManyToMany?報(bào)錯(cuò)StackOverflowError的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
JavaWeb pageContext對(duì)象原理解析
這篇文章主要介紹了JavaWeb pageContext對(duì)象原理解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02

