在日常开发中,Excel文件读取是一个常见的需求,无论是数据处理、报表分析还是数据导入导出,都需要高效地处理Excel文件。本文将介绍几种Java中高效读取Excel数据的方法,并对比它们的性能特点。
一、常用技术选型1. Apache POIApache POI是Apache软件基金会的开源项目,提供了对Microsoft Office格式文件的读写功能,是Java领域最流行的Excel处理库。
2. EasyExcel阿里开源的EasyExcel基于POI进行封装优化,解决了POI的内存消耗问题,特别适合大数据量的读取。
二、Apache POI读取方案1. 依赖引入代码语言:javascript复制
2. 事件模式读取(SAX方式)对于大型Excel文件,推荐使用事件模式读取,避免内存溢出:
代码语言:javascript复制public class ExcelSAXReader {
public void readExcel(String filePath) {
try (InputStream is = new FileInputStream(filePath)) {
Workbook workbook = WorkbookFactory.create(is);
Sheet sheet = workbook.getSheetAt(0);
// 使用迭代器遍历行
Iterator
while (rowIterator.hasNext()) {
Row row = rowIterator.next();
processRow(row);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void processRow(Row row) {
Iterator
while (cellIterator.hasNext()) {
Cell cell = cellIterator.next();
System.out.print(getCellValue(cell) + "\t");
}
System.out.println();
}
private String getCellValue(Cell cell) {
return switch (cell.getCellType()) {
case STRING -> cell.getStringCellValue();
case NUMERIC ->
DateUtil.isCellDateFormatted(cell) ?
cell.getDateCellValue().toString() :
String.valueOf(cell.getNumericCellValue());
case BOOLEAN -> String.valueOf(cell.getBooleanCellValue());
case FORMULA -> cell.getCellFormula();
default -> "";
};
}
}3. 流式读取(针对.xlsx格式)对于超大文件,使用XSSF and SAX方式:
代码语言:javascript复制public class ExcelStreamReader {
public void processLargeExcel(String filename) throws Exception {
OPCPackage pkg = OPCPackage.open(filename);
XSSFReader reader = new XSSFReader(pkg);
SharedStringsTable sst = reader.getSharedStringsTable();
XMLReader parser = fetchSheetParser(sst);
InputStream sheet2 = reader.getSheet("rId1");
InputSource sheetSource = new InputSource(sheet2);
parser.parse(sheetSource);
sheet2.close();
}
private XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException {
XMLReader parser = XMLReaderFactory.createXMLReader();
ContentHandler handler = new SheetHandler(sst);
parser.setContentHandler(handler);
return parser;
}
private static class SheetHandler extends DefaultHandler {
// 实现具体的SAX解析逻辑
}
}三、EasyExcel读取方案1. 引入依赖代码语言:javascript复制
2. 简单读取示例代码语言:javascript复制// 定义数据模型
@Data
public class DemoData {
@ExcelProperty(index = 0)
private String name;
@ExcelProperty(index = 1)
private Integer age;
@ExcelProperty(index = 2)
private Date birthday;
}
// 读取监听器
public class DemoDataListener extends AnalysisEventListener
private static final int BATCH_COUNT = 100;
private List
@Override
public void invoke(DemoData data, AnalysisContext context) {
cachedDataList.add(data);
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
cachedDataList = new ArrayList<>(BATCH_COUNT);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
saveData();
}
private void saveData() {
// 批量保存数据
if (!cachedDataList.isEmpty()) {
// 这里实现数据持久化逻辑
System.out.println("保存" + cachedDataList.size() + "条数据");
cachedDataList.clear();
}
}
}
// 使用EasyExcel读取
public class EasyExcelReader {
public void readExcel(String filePath) {
EasyExcel.read(filePath, DemoData.class, new DemoDataListener())
.sheet()
.doRead();
}
}3. 复杂读取场景代码语言:javascript复制// 读取多个sheet
EasyExcel.read(filePath)
.sheet(0) // 第一个sheet
.doRead();
EasyExcel.read(filePath)
.sheet("Sheet1") // 按名称读取
.doRead();
// 读取部分列
EasyExcel.read(filePath, DemoData.class, new DemoDataListener())
.extraRead(CellExtraTypeEnum.COMMENT) // 读取注释
.sheet()
.doRead();
// 自定义转换器
public class CustomConverter implements Converter
@Override
public String convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
// 自定义转换逻辑
return "转换后的数据:" + cellData.getStringValue();
}
}四、性能优化建议1. 内存优化使用流式读取代替DOM式读取及时清理不再使用的对象引用设置合适的JVM内存参数2. 读取策略优化按需读取,避免一次性加载全部数据使用缓存机制减少重复IO操作批量处理数据,减少数据库操作次数3. 代码层面优化代码语言:javascript复制// 使用try-with-resources确保资源释放
try (InputStream is = new FileInputStream(file);
Workbook workbook = WorkbookFactory.create(is)) {
// 处理逻辑
}
// 避免在循环中创建对象
CellStyle cellStyle = workbook.createCellStyle();
for (Row row : sheet) {
Cell cell = row.createCell(0);
cell.setCellStyle(cellStyle); // 复用样式对象
}
// 使用数据类型检测优化处理逻辑
if (cell.getCellType() == CellType.NUMERIC) {
if (DateUtil.isCellDateFormatted(cell)) {
// 处理日期
} else {
// 处理数字
}
}五、性能对比方案
内存占用
读取速度
适用场景
POI DOM模式
高
慢
小文件,需要完整访问
POI SAX模式
低
快
大文件,顺序读取
EasyExcel
极低
很快
超大文件,批量处理
六、总结选择合适的Excel读取方案需要根据具体场景决定:
对于小文件(<10MB),可以使用POI的标准API,代码简单易用对于中等文件(10-100MB),建议使用POI的流式API对于大文件(>100MB),强烈推荐使用EasyExcel无论选择哪种方案,都应该注意资源释放、异常处理和内存管理,确保程序的稳定性和性能。希望本文能帮助你在实际开发中选择合适的Excel读取方案。
注意事项:
处理Excel时注意日期格式的转换问题考虑空单元格和异常数据的处理注意字符编码问题,特别是中文处理大型文件处理时建议添加进度指示器