这篇文章主要是对 第01篇、第02篇 网友的一些使用误区给与回复
总结:如果你的Excel数据很少,大致2000行以下,20列以内,那么用POI的dom方式够用。
如果你的行数经常大于1w或甚至百万行,那么POI读取Excel 必须或者说只能用SAX方式,因为只有这方法不会内存溢出。
但是看到网友针对前2篇的文章,即第01篇、第02篇 给与我的反馈来看,网友并没有理解它的使用方式,以及为什么构造函数要设计成如下形式:
public MyExcel2007ForPaging_high(String filename, int startRow, int endRow) throws Exception {
if (StringUtils.isBlank(filename))
throw new Exception("文件名不能空");
this.filename = filename;
this.startRow = startRow;
this.endRow = endRow;
processFirstSheet();
}
就像我开头说的,如果你的Excel数据量少,根本没必要用sax方式,那既然你用sax方式,肯定是因为文件太大,数据太多,dom方式无法读取的问题这才必须使用sax方式来读取。
POI在dom和sax的实现上本质上不同,dom方式不管你实际获取的数据是多还是少,在读取时都是将文件全部内容读取到内存,并建立dom结构,这是最让开发人员头疼的,因为内存溢出和实际获取数据无关,只和文件大小有关;
而sax采用直接干预读取流的方式,只获取自己需要的部分即可,这也是性能提升和不会内存溢出的原因。因为内存溢出只和实际你要获取的数据有关,和文件大小无关。那我问你,如果你实际要一次性获取10G数据,程序会报内存溢出不?当然会!因为这和poi的sax实现方式没关系,是你java虚拟机分配内存的问题!
回到上文的构造函数, 构造函数如此设计的目的本来就不是让你一次性把Excel数据都取出来的!应该按照Excel中的总行数进行逻辑分页,一页页的读取,首先针对每页进行处理业务逻辑,之后释放内存,然后读取下一页的数据...以此类推...这样就能保证内存中只有一页的数据函数等待处理,而不是全部数据全部弄到内存。
那么,上两篇的文章的函数如何使用呢?或者说如何将文件的数据进行逻辑分页呢?
下面贴出我们项目使用的分页接口,函数名称见名知义,不多解释。能用则用,不能用,聪明的网友就自己写吧。^_^
代码里涉及了MyExcel2007ForMaxRow 、MyExcel2007ForPaging_high这两个类,这里不贴了,在前面3篇文章里有贴的。
import java.io.File;
import java.util.LinkedList;
import java.util.List;
public class SaxPOI2 {
private static SaxPOI2 instance = null;
public final static int TITLELINE_ROW_INDEX = 0;
private SaxPOI2() {
}
public static SaxPOI2 getInstance() {
if(instance==null){
instance = new SaxPOI2();
}
return instance;
}
/**
* 获取文件标题
* @param file
* @param rows
* @return
* @throws Exception
*/
public static List<List<String>> getTitles(File file,int sumOfrows) throws Exception{
//由于这里标题就是具体内容,所以应该把标题当做要获取的数据
return SaxPOI2.getPagingData(file, 1, sumOfrows, sumOfrows,0);
}
/**
* 利用POI的sax方式分页读取2007的Excel
*
* @param file
* @param page 页号 从1开始
* @param rows 页内行数
* @param totalRows 实际的总行数(去除标题)
* @param outOfTitleRow 忽略标题行的行数,负数忽略,0代表包含标题,正值代表跳过的标题数
* @return
* @throws Exception
*/
public static List<List<String>> getPagingData(File file, int page,
int rows,int totalRows,int outOfTitleRow) throws Exception {
// 总记录行数(除去标题)
int sumOfRows = totalRows;
// SAX解析方式第一行是1,不是0
int headLineRowNum = SaxPOI2.TITLELINE_ROW_INDEX;
if(outOfTitleRow>0){
headLineRowNum += outOfTitleRow;
}
// 循环的默认起始和结束
int startRow = headLineRowNum + 1;
int endRow = startRow + rows - 1;
// 最后一行的行号
int lastRowNum = sumOfRows + headLineRowNum;
int odd = sumOfRows - ((page - 1) * rows);// 本页之前的所有记录
if ((page - 1) * rows >= sumOfRows) {// 本页之前的所有记录已经超过总数了
return new LinkedList<List<String>>();
} else if (odd > 0 && odd <= rows) {// 剩下的行数不够本页显示数
startRow = (headLineRowNum + 1) + (page - 1) * rows;
endRow = lastRowNum;
} else {// 所剩行数充足
startRow = headLineRowNum + (page - 1) * rows + 1;
endRow = headLineRowNum + page * rows;
}
return new MyExcel2007ForPaging_high(file.getPath(), startRow, endRow).getMyDataList();
}
/**
* 采用SAX读取2007版的xlsx形式的Excel的最大行
*
* @param f
* @return
* @throws Exception
*/
public static long getSize4TheFile(File f) throws Exception {
MyExcel2007ForMaxRow reader = new MyExcel2007ForMaxRow(f);
return reader.maxRow - (SaxPOI2.TITLELINE_ROW_INDEX + 1);
}
}