废话不多说,直接贴demo
public void importExcel(File file, int batchSize, int threadCount) throws Exception {
// 创建 Excel 读取器
ExcelReader reader = new ExcelReaderBuilder(new FileInputStream(file), null, new ImportDataListener())
.headRowNumber(1)
.build();
// 获取表格中总行数
ReadSheet sheet = reader.excelExecutor().sheet(0);
Long totalRows = sheet.getHeadRowNumber() + sheet.getTotalRowNumber() - 1;
// 计算分片大小和分片数量
int shardSize = batchSize * threadCount;
int shardNum = (int) Math.ceil(totalRows * 1.0 / shardSize);
// 创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
// 使用 CountDownLatch 控制主线程的等待
CountDownLatch countDownLatch = new CountDownLatch(shardNum);
try {
// 分片读取并处理 Excel 数据
for (int i = 0; i < shardNum; i++) {
// 计算当前分片的起始位置和结束位置
int startIndex = i * shardSize;
int endIndex = Math.min(startIndex + shardSize, totalRows.intValue());
// 提交任务到线程池中处理
executorService.submit(() -> {
try {
List<List<String>> data = reader.read(sheet, ReadRowHolder.class,
new ReadRowHolder(startIndex, endIndex)).getData();
// 处理数据,具体处理逻辑可根据自己的业务需求进行编写
} catch (Exception e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
});
}
// 主线程等待所有任务完成
countDownLatch.await();
} finally {
// 关闭线程池和 Excel 读取器
executorService.shutdown();
reader.close();
}
}
/**
* 用于 Excel 数据读取时分片读取数据
*/
private static class ReadRowHolder implements RowMapper<List<String>> {
private int startIndex;
private int endIndex;
public ReadRowHolder(int startIndex, int endIndex) {
this.startIndex = startIndex;
this.endIndex = endIndex;
}
@Override
public List<String> mapRow(Row row) {
int rowNum = row.getRowNum() + 1;
if (rowNum < startIndex || rowNum > endIndex) {
return null; // 非当前分片数据行,忽略
}
List<String> rowData = new ArrayList<>();
int colNum = row.getLastCellNum(); // 获取当前行最后一个单元格的列号
for (int i = 0; i < colNum; i++) {
Cell cell = row.getCell(i);
String cellValue = cell == null ? null : cell.getStringCellValue();
rowData.add(cellValue);
}
return rowData; // 返回读取到的数据
}
}
/**
* 用于 Excel 数据读取时处理每一行数据
*/
private static class ImportDataListener extends AnalysisEventListener<List<String>> {
@Override
public void invoke(List<String> rowData, AnalysisContext context) {
// 处理数据,具体处理逻辑可根据自己的业务需求进行编写
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 数据读取完成后的操作,如释放资源等
}
}
importExcel
方法接收一个 Excel 文件对象、批大小 batchSize
和线程数 threadCount
作为参数。首先,使用 ExcelReader
对象读取 Excel 文件,并计算出总行数和分片大小和数量;然后,创建一个固定数量的线程池,使用 CountDownLatch
控制主线程等待所有任务完成;最后,循环迭代分片区间,将分片任务提交到线程池中处理。在每个任务中,使用 ReadRowHolder
对象实现分片读取 Excel 数据,并使用 ImportDataListener
处理每一行数据。