java poi sax方式处理大数据量excel文件
系统需要用到一个导入excel文件的功能,使用poi组件常规方式读取excel时,内存耗尽,OutOfMemoryError,或者读取非常慢
所以写了一个工具类,使用poisax方式读取excel,速度快很多,内存消耗可以接受。
测试结果如下:
.xlsx文件,35M大小,总4个sheel,
只读取第一个,37434行,54列
总行数:37434
读取耗时:39秒
打印耗时:17秒
主要代码如下:
ExcelUtils.class主入口
packagecom.xxx.bi.utils.excel;
importjava.util.List;
importjava.util.Objects;
importorg.apache.commons.lang3.StringUtils;
importcom.google.common.collect.Lists;
publicclassExcelUtils{
/**logger日志.*/
//publicstaticfinalLoggerLOGGER=Logger.getLogger(ExcelUtils.class);
publicExcelUtils(){
}
/**
*获取excel的表头
*
*@paramfilePath
*文件路径
*@paramheaderNum
*表头所在行数
*@return
*/
publicstaticListgetHeader(StringfilePath,intheaderNum){
if(StringUtils.isBlank(filePath)){
thrownewIllegalArgumentException("传入文件路径不能为空");
}
if(Objects.isNull(headerNum)||headerNum<1){
headerNum=1;
}
try{
returnLargeExcelFileReadUtil.getRowFromSheetOne(filePath,headerNum);
}catch(Exceptione){
//LOGGER.info("获取excel["+filePath+"]表头失败,原因:",e);
e.printStackTrace();
}
returnLists.newArrayList();
}
/**
*获取excel的所有数据
*所有数据类型都是String
*会以第一行数据的列数为总列数,所以第一行的数据必须都不为空,否则可能出java.lang.IndexOutOfBoundsException
*
*@paramfilePath
*文件路径
*@paramheaderNum
*表头所在行数
*@return
*/
publicstaticList>getAllData(StringfilePath){
if(StringUtils.isBlank(filePath)){
thrownewIllegalArgumentException("传入文件路径不能为空");
}
try{
returnLargeExcelFileReadUtil.getRowsFromSheetOne(filePath);
}catch(Exceptione){
//LOGGER.info("获取excel["+filePath+"]表头失败,原因:",e);
e.printStackTrace();
}
returnLists.newArrayList();
}
publicstaticvoidmain(String[]args){
longstart=System.currentTimeMillis();
Stringfilepath="C:/Users/Administrator/Desktop/05-作业调配表-快递.xlsx";
//Listresult=ExcelUtils.getHeader(filepath,1);
//for(Stringcol:result){
//System.out.println(col);
//}
List>result=ExcelUtils.getAllData(filepath);
longend=System.currentTimeMillis();
for(Listlist:result){
System.out.println(list.toString());
}
longend1=System.currentTimeMillis();
try{
Thread.sleep(1000l);
}catch(InterruptedExceptione){
e.printStackTrace();
}
System.err.println("总行数:"+result.size());
System.err.println(("读取耗时:"+(end-start)/1000)+"秒");
System.err.println(("打印耗时:"+(end1-end)/1000)+"秒");
}
}
LargeExcelFileReadUtil.class真正的工具类
packagecom.xxx.bi.utils.excel;
importjava.io.InputStream;
importjava.util.List;
importjava.util.Objects;
importorg.apache.log4j.Logger;
importorg.apache.poi.openxml4j.opc.OPCPackage;
importorg.apache.poi.xssf.eventusermodel.XSSFReader;
importorg.apache.poi.xssf.model.SharedStringsTable;
importorg.xml.sax.InputSource;
importorg.xml.sax.XMLReader;
importorg.xml.sax.helpers.XMLReaderFactory;
publicclassLargeExcelFileReadUtil{
/**logger日志.*/
publicstaticfinalLoggerLOGGER=Logger.getLogger(LargeExcelFileReadUtil.class);
//处理一个sheet
publicstaticListgetRowFromSheetOne(Stringfilename,IntegerrowNum)throwsException{
InputStreaminputStream=null;
OPCPackagepkg=null;
SingleRowHandlersingleRowHandler=null;
try{
pkg=OPCPackage.open(filename);
XSSFReaderr=newXSSFReader(pkg);
SharedStringsTablesst=r.getSharedStringsTable();
singleRowHandler=newSingleRowHandler(sst,rowNum);
XMLReaderparser=XMLReaderFactory.createXMLReader("com.sun.org.apache.xerces.internal.parsers.SAXParser");
parser.setContentHandler(singleRowHandler);
inputStream=r.getSheet("rId1");
InputSourcesheetSource=newInputSource(inputStream);
parser.parse(sheetSource);
returnsingleRowHandler.getRow();
}catch(Exceptione){
Stringmessage=e.getMessage();
if(Objects.nonNull(rowNum)&&Objects.nonNull(singleRowHandler)
&&SingleRowHandler.FINISH_ROW_MESSAGE.equalsIgnoreCase(message)){
//获取某一行数据完成,暂时不知道怎么能终止excel解析,直接抛出了异常,实际是成功的
returnsingleRowHandler.getRow();
}
throwe;
}finally{
if(Objects.nonNull(pkg)){
pkg.close();
}
if(Objects.nonNull(inputStream)){
inputStream.close();
}
}
}
//处理一个sheet
publicstaticList>getRowsFromSheetOne(Stringfilename)throwsException{
InputStreaminputStream=null;
OPCPackagepkg=null;
MultiRowHandlermultiRowHandler=null;
try{
pkg=OPCPackage.open(filename);
XSSFReaderr=newXSSFReader(pkg);
SharedStringsTablesst=r.getSharedStringsTable();
multiRowHandler=newMultiRowHandler(sst);
XMLReaderparser=XMLReaderFactory.createXMLReader("com.sun.org.apache.xerces.internal.parsers.SAXParser");
parser.setContentHandler(multiRowHandler);
inputStream=r.getSheet("rId1");
InputSourcesheetSource=newInputSource(inputStream);
parser.parse(sheetSource);
returnmultiRowHandler.getRows();
}catch(Exceptione){
throwe;
}finally{
if(Objects.nonNull(pkg)){
pkg.close();
}
if(Objects.nonNull(inputStream)){
inputStream.close();
}
}
}
}
SingleRowHandler.class当行处理类,可以只获取表头或表格中的某一行数据
packagecom.xxx.bi.utils.excel;
importjava.util.ArrayList;
importjava.util.List;
importjava.util.Objects;
importjava.util.regex.Pattern;
importorg.apache.poi.xssf.model.SharedStringsTable;
importorg.apache.poi.xssf.usermodel.XSSFRichTextString;
importorg.xml.sax.Attributes;
importorg.xml.sax.SAXException;
importorg.xml.sax.helpers.DefaultHandler;
publicclassSingleRowHandlerextendsDefaultHandler{
publicfinalstaticStringFINISH_ROW_MESSAGE="rowdataprocessfinish";
privateIntegerrowNum=null;//rowNum不为空时则标示只需要获取这一行的数据
privateintcurRowNum=1;
privateStringcellType="";
privateSharedStringsTablesst;
privateStringlastContents;
privatebooleannextIsString;
privateStringcellPosition;
privateListrow=newArrayList<>();
publicListgetRow(){
returnrow;
}
publicSingleRowHandler(SharedStringsTablesst,IntegerrowNum){
this.sst=sst;
this.rowNum=rowNum;
}
publicvoidstartElement(Stringuri,StringlocalName,Stringname,Attributesattributes)throwsSAXException{
if(name.equals("c")){
cellPosition=attributes.getValue("r");
//这是一个新行
if(Pattern.compile("^A[0-9]+$").matcher(cellPosition).find()){
curRowNum=Integer.valueOf(cellPosition.substring(1));
}
cellType="";
cellType=attributes.getValue("t");
if("s".equals(cellType)){
nextIsString=true;
}else{
nextIsString=false;
}
}
//清楚缓存内容
lastContents="";
if(Objects.nonNull(rowNum)&&curRowNum>rowNum){
//获取某一行数据完成,暂时不知道怎么能终止excel解析,直接抛出了异常,实际是成功的
thrownewSAXException(FINISH_ROW_MESSAGE);
}
}
publicvoidendElement(Stringuri,StringlocalName,Stringname)throwsSAXException{
if(nextIsString){
intidx=Integer.parseInt(lastContents);
lastContents=newXSSFRichTextString(sst.getEntryAt(idx)).toString();
nextIsString=false;
}
if(name.equals("v")){
if(Objects.isNull(rowNum)||rowNum==curRowNum){
row.add(lastContents);
}
}
}
publicvoidcharacters(char[]ch,intstart,intlength)throwsSAXException{
lastContents+=newString(ch,start,length);
}
}
MultiRowHandler.class获取excel所有行的数据
packagecom.xxx.bi.utils.excel; importjava.util.ArrayList; importjava.util.List; importjava.util.Objects; importjava.util.regex.Pattern; importorg.apache.commons.lang3.StringUtils; importorg.apache.poi.xssf.model.SharedStringsTable; importorg.apache.poi.xssf.usermodel.XSSFRichTextString; importorg.xml.sax.Attributes; importorg.xml.sax.SAXException; importorg.xml.sax.helpers.DefaultHandler; /** *获取完整excel数据的handler
* *@authorAdministrator * */ publicclassMultiRowHandlerextendsDefaultHandler{ privateintcurRowNum=0;//行号,从1开始 privateintcurColIndex=-1;//列索引,从0开始 privateintcolCnt=0;//列数,取第一行列数做为列总数 privateStringcellType=""; privateSharedStringsTablesst; privateStringlastContents; privatebooleannextIsString; privateStringcellPosition; privateListhead=null; privateList curRowData=null; privatebooleancurRowIsBlank=true;//当前是个空行 privateList >rows=newArrayList<>(); publicList
>getRows(){ returnrows; } publicMultiRowHandler(SharedStringsTablesst){ this.sst=sst; } @Override publicvoidstartElement(Stringuri,StringlocalName,Stringname,Attributesattributes)throwsSAXException{ if(name.equals("c")){ cellPosition=attributes.getValue("r"); curColIndex=getColIndex(cellPosition); //这是一个新行 if(isNewRow(cellPosition)){ curRowNum=getRowNum(cellPosition); if(2==curRowNum&&Objects.nonNull(curRowData)){ head=curRowData; colCnt=head.size(); } curRowData=getBlankRow(colCnt); } cellType=""; cellType=attributes.getValue("t"); if("s".equals(cellType)){ nextIsString=true; }else{ nextIsString=false; } } //清楚缓存内容 lastContents=""; } privatebooleanisNewRow(StringcellPosition){ //坐标以A开头,后面跟数字或者坐标行和当前行不一致的 booleannewRow=Pattern.compile("^A[0-9]+$").matcher(cellPosition).find(); if(!newRow){ intcellRowNum=getRowNum(cellPosition); newRow=(cellRowNum!=curRowNum); } returnnewRow; } /** *根据列坐标获取行号,从1开始,返回0时标示出错 * *@paramcellPosition *列坐标,为A1,B23等 *@return行号,从1开始,返回0是为失败 */ privatestaticintgetRowNum(StringcellPosition){ StringstrVal=Pattern.compile("[^0-9]").matcher(cellPosition).replaceAll("").trim();//获取坐标中的数字 if(StringUtils.isNotBlank(strVal)){ returnInteger.valueOf(strVal); } return0; } /** *根据列坐标返回当前列索引,从0开始,返回-1时标示出错
*A1->0;B1->1...AA1->26 * *@paramcellPosition *列坐标,为A1,B23等 *@return列索引,从0开始,返回-1是为失败,A1->0;B1->1...AA1->26 */ privatestaticintgetColIndex(StringcellPosition){ intindex=-1; intnum=65;//A的Unicode码 intlength=cellPosition.length(); for(inti=0;igetBlankRow(intcnt){ List result=newArrayList<>(cnt); for(inti=0;i 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。