我们在读取excel过程中,通常使用的方案为POI的普通读取,但是遇到大数据量excel (比如一个excel超过20M,上10万行上百列等),此时一般代码会报内存溢出(OOM);
以下为一种解决方案(亲测有效):
首先引入的依赖:
<!--大批量excel导入依赖开始-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>4.0.0</version>
</dependency>
<!-- 读取大量excel数据时使用 -->
<dependency>
<groupId>com.monitorjbl</groupId>
<artifactId>xlsx-streamer</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.0.0</version>
</dependency>
<!--大批量excel导入依赖结束-->
Java代码:
/**
* 大批量数据读取 15W以上
* 思路:采用分段缓存,防止出现OOM的情况
* 格式限制:必须使用xlsx的格式,调用前需判断格式
*/
public static List<Map<String,Object>> readBigExcel(MultipartFile file,String rowname,int stasheetNum,int starowNum,int stacolumn)throws Exception{
//定义返回值
List<Map<String,Object>> resultList=new ArrayList<Map<String,Object>>();
InputStream inputStream=file.getInputStream();
try( Workbook wk= StreamingReader.builder()
.rowCacheSize(100) //缓存到内存中的行数,默认是10
.bufferSize(4096) //读取资源时,缓存到内存的字节大小,默认是1024
.open(inputStream); ){ //打开资源,必须,可以是InputStream或者是File,注意:只能打开XLSX格式的文件
Sheet sheet = wk.getSheetAt(stasheetNum);
String[] rownameSplit=rowname.split(",");
int columnlength=rownameSplit.length;
Cell cell=null;//定义单元格
//遍历所有的行()
for (Row row :sheet) {
//row=sheet.getRow(i);//获取当前循环的行数据(因为只缓存了部分数据,所以不能用getRow来获取)此处采用增强for循环直接获取row对象
Map<String, Object> paramMap = new HashMap<String, Object>();//定义一个map做数据接收
if (row.getRowNum() >= starowNum) { //从设定的行开始取值
//System.out.println("开始遍历第" + row.getRowNum() + "行数据:");
//对当前行逐列进行循环取值
for (int j = stacolumn; j < columnlength; j++) {
/**
注:MyUtil.isEmpty为工具类判空方法,可替换为自己的判空工具
**/
if (MyUtil.isEmpty(row)) {
paramMap.put(rownameSplit[j], null);//将单元格值放入map
} else {
cell = row.getCell(j);//获取单元格数据
if (MyUtil.isEmpty(cell) && cell.getCellType() == CellType.BLANK) {
paramMap.put(rownameSplit[j], null);//将单元格值放入map
} else {
paramMap.put(rownameSplit[j], cell.getStringCellValue());//将单元格值放入map
}
}
}
//一行循环完成,将map存入list
resultList.add(paramMap);
}
}
}
return resultList;
}
参数解释:
file:前台页面传递的文件
rowname:为读取的每个列起的名字。如excel文件列为 姓名,身份证,性别 ;此处可传字符串"name,card,sex";
stasheetNum:读取的工作簿 从0开始
starowNum: 开始读取的行 从0开始
stacolumn:开始读取的列 从0开始
返回值:List<Map<String,Object>> 将读取的内容 封装为到一个list中,并以map形式存放;
可根据实际情况进行调整:
注:POI版本需4以上,excel只能读取xlsx格式;如有更好方案 欢迎留言分享