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"; //List
result=ExcelUtils.getHeader(filepath,1); //for(Stringcol:result){ //System.out.println(col); //} List >result=ExcelUtils.getAllData(filepath); longend=System.currentTimeMillis(); for(List
list: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<>(); publicList getRow(){ 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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。