Java 读取 Excel 文件
- 一、前置准备:引入依赖
- 方案 1:Apache POI(功能全,兼容所有Excel版本)
- 方案 2:EasyExcel(阿里开源,低内存,推荐大数据量)
- 二、方案 1:Apache POI 读取 Excel(通用场景)
- 场景 1:读取所有sheet的所有单元格(基础版)
- 场景 2:读取指定sheet和指定行(精准读取)
- 三、方案 2:EasyExcel 读取 Excel(高性能,推荐大数据量)
- 步骤 1:定义数据实体(与Excel表头映射)
- 步骤 2:自定义监听器(处理读取到的数据)
- 步骤 3:读取Excel文件(核心代码)
- 四、关键注意事项
- 1. 文件路径与权限
- 2. 版本兼容
- 3. 性能优化
- 4. 日期/数字格式
- 五、两种方案对比
- 六、常见问题解决
- 1. `FileNotFoundException`
- 2. 内存溢出(OOM)
- 3. 日期读取为数字
Java 读取 Excel 文件核心依赖Apache POI(兼容.xls(Excel 97-2003)和.xlsx(Excel 2007+))或EasyExcel(阿里开源,低内存、高性能),以下是两种主流方案的完整实现,覆盖「读取简单单元格、读取指定sheet、读取表头+数据」等场景。
一、前置准备:引入依赖
方案 1:Apache POI(功能全,兼容所有Excel版本)
Maven 依赖(需同时引入poi和poi-ooxml,分别对应.xls和.xlsx):
<dependencies><!-- 核心依赖:处理 .xls --><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.5</version><!-- 推荐最新稳定版 --></dependency><!-- 处理 .xlsx --><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.5</version></dependency><!-- 可选:简化日期格式处理 --><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>4.1.2</version></dependency></dependencies>方案 2:EasyExcel(阿里开源,低内存,推荐大数据量)
Maven 依赖(仅需核心包,自动兼容.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(通用场景)
场景 1:读取所有sheet的所有单元格(基础版)
importorg.apache.poi.ss.usermodel.*;importorg.apache.poi.xssf.usermodel.XSSFWorkbook;importorg.apache.poi.hssf.usermodel.HSSFWorkbook;importjava.io.FileInputStream;importjava.io.IOException;publicclassPOIExcelReader{publicstaticvoidmain(String[]args){StringfilePath="D:/test.xlsx";// 替换为你的Excel文件路径// 区分 .xls 和 .xlsxWorkbookworkbook=null;try(FileInputStreamfis=newFileInputStream(filePath)){if(filePath.endsWith(".xlsx")){workbook=newXSSFWorkbook(fis);// .xlsx}elseif(filePath.endsWith(".xls")){workbook=newHSSFWorkbook(fis);// .xls}else{thrownewIllegalArgumentException("不支持的Excel格式");}// 遍历所有sheetfor(Sheetsheet:workbook){System.out.println("===== Sheet名称:"+sheet.getSheetName()+" =====");// 遍历所有行(跳过表头:从第1行开始,rowNum=1)for(Rowrow:sheet){// 遍历该行所有单元格for(Cellcell:row){// 获取单元格值(统一格式)StringcellValue=getCellValue(cell);System.out.print(cellValue+"\t");}System.out.println();// 换行}}}catch(IOExceptione){e.printStackTrace();}finally{if(workbook!=null){try{workbook.close();}catch(IOExceptione){e.printStackTrace();}}}}// 工具方法:统一处理不同类型的单元格值privatestaticStringgetCellValue(Cellcell){if(cell==null){return"";}switch(cell.getCellType()){caseSTRING:// 字符串returncell.getStringCellValue();caseNUMERIC:// 数字/日期if(DateUtil.isCellDateFormatted(cell)){// 日期类型returncell.getDateCellValue().toString();}else{// 数字类型(避免科学计数法)returnString.valueOf(cell.getNumericCellValue());}caseBOOLEAN:// 布尔值returnString.valueOf(cell.getBooleanCellValue());caseFORMULA:// 公式returncell.getCellFormula()+" = "+cell.getCachedFormulaResultType();caseBLANK:// 空单元格return"";default:return"";}}}场景 2:读取指定sheet和指定行(精准读取)
// 读取指定sheet(索引从0开始,或按名称)Sheetsheet=workbook.getSheetAt(0);// 第一个sheet// 或 Sheet sheet = workbook.getSheet("用户数据"); // 按名称// 读取指定行(如第2行,rowNum=1)RowtargetRow=sheet.getRow(1);if(targetRow!=null){// 读取指定单元格(如第3列,cellNum=2)CelltargetCell=targetRow.getCell(2);Stringvalue=getCellValue(targetCell);System.out.println("指定单元格值:"+value);}// 遍历有效行(跳过空行)intlastRowNum=sheet.getLastRowNum();// 最后一行索引for(introwNum=1;rowNum<=lastRowNum;rowNum++){Rowrow=sheet.getRow(rowNum);if(row==null){continue;// 跳过空行}// 读取该行单元格Stringname=getCellValue(row.getCell(0));// 第1列:姓名Stringage=getCellValue(row.getCell(1));// 第2列:年龄System.out.println("姓名:"+name+",年龄:"+age);}三、方案 2:EasyExcel 读取 Excel(高性能,推荐大数据量)
EasyExcel 无需加载整个Excel到内存,适合读取十万级以上数据,核心是通过「监听器」逐行读取。
步骤 1:定义数据实体(与Excel表头映射)
importcom.alibaba.excel.annotation.ExcelProperty;importlombok.Data;// 对应Excel的表头:姓名、年龄、手机号@Data// Lombok注解,自动生成get/setpublicclassUserExcelDTO{// value:Excel表头名称,index:列索引(可选)@ExcelProperty(value="姓名",index=0)privateStringname;@ExcelProperty(value="年龄",index=1)privateIntegerage;@ExcelProperty(value="手机号",index=2)privateStringphone;}步骤 2:自定义监听器(处理读取到的数据)
importcom.alibaba.excel.context.AnalysisContext;importcom.alibaba.excel.event.AnalysisEventListener;importjava.util.ArrayList;importjava.util.List;// 自定义监听器,逐行读取数据并存储publicclassUserExcelListenerextendsAnalysisEventListener<UserExcelDTO>{// 存储读取到的数据privateList<UserExcelDTO>dataList=newArrayList<>();// 每读取一行数据触发@Overridepublicvoidinvoke(UserExcelDTOuser,AnalysisContextcontext){dataList.add(user);System.out.println("读取到数据:"+user);// 可在此处批量处理(如每1000条插入数据库)if(dataList.size()>=1000){handleData();// 处理数据dataList.clear();// 清空}}// 所有数据读取完成后触发@OverridepublicvoiddoAfterAllAnalysed(AnalysisContextcontext){handleData();// 处理剩余数据System.out.println("Excel读取完成,总数据量:"+dataList.size());}// 数据处理逻辑(如插入数据库)privatevoidhandleData(){if(!dataList.isEmpty()){// TODO: 批量插入数据库/业务处理System.out.println("批量处理"+dataList.size()+"条数据");}}// 获取读取到的所有数据publicList<UserExcelDTO>getDataList(){returndataList;}}步骤 3:读取Excel文件(核心代码)
importcom.alibaba.excel.EasyExcel;importjava.util.List;publicclassEasyExcelReader{publicstaticvoidmain(String[]args){StringfilePath="D:/test.xlsx";// 初始化监听器UserExcelListenerlistener=newUserExcelListener();// 读取Excel(指定文件路径、实体类、监听器)EasyExcel.read(filePath,UserExcelDTO.class,listener).sheet("用户数据")// 指定sheet名称(可选,默认第一个).headRowNumber(1)// 表头行数(默认1行).doRead();// 执行读取// 获取所有数据List<UserExcelDTO>dataList=listener.getDataList();System.out.println("最终读取到的数据:"+dataList);}}四、关键注意事项
1. 文件路径与权限
- 确保文件路径无中文/空格(避免
FileNotFoundException); - 若读取服务器文件,需保证Java进程有文件读取权限(如Linux下
chmod 755)。
2. 版本兼容
.xls(HSSFWorkbook)最大支持65536行,.xlsx(XSSFWorkbook)无行数限制;- EasyExcel 自动兼容两种格式,无需手动区分。
3. 性能优化
- Apache POI 读取大数据量Excel易内存溢出,需用
SXSSFWorkbook(流式读取); - EasyExcel 天生适合大数据量,无需额外配置。
4. 日期/数字格式
- Apache POI 需手动判断日期格式(
DateUtil.isCellDateFormatted); - EasyExcel 可通过
@ExcelProperty(converter = DateConverter.class)自定义格式转换。
五、两种方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Apache POI | 功能全、兼容所有Excel特性 | 大数据量易内存溢出、代码繁琐 | 小数据量、需操作复杂Excel(公式/宏) |
| EasyExcel | 低内存、代码简洁、高性能 | 不支持宏/复杂公式 | 大数据量、普通数据读取(推荐) |
六、常见问题解决
1.FileNotFoundException
- 检查文件路径是否正确(绝对路径/相对路径);
- 检查文件是否被占用(如Excel未关闭)。
2. 内存溢出(OOM)
- Apache POI:改用
SXSSFWorkbook流式读取; - 优先使用 EasyExcel。
3. 日期读取为数字
- Apache POI:通过
DateUtil.isCellDateFormatted判断并转换; - EasyExcel:配置日期转换器。
核心原则:小数据量/复杂Excel用 Apache POI,大数据量/普通读取用 EasyExcel;读取时务必处理空单元格和格式转换,避免空指针/格式错误。