最近需要处理一个比较大的excel文件,但是poi在处理文件时会抛出OOM导致程序崩溃,查看官方文档看到可以以流式的方式读取excel避免读取大文件时的OOM。本文主要记述xls的处理。

环境模拟

先准备一个大的excel文件(xls大小8M),再将jvm的heap缩小到100m(JVM 参数 -Xmx100m)用于模拟OOM

并使用参数在OOM时dump内存 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d://dump.hprof

使用HSSF读取

    public static void main(String [] args) throws IOException {
InputStream is = new FileInputStream("d://large.xls");
Workbook wb = new HSSFWorkbook(is);
}

运行之后

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.lang.Integer.valueOf(Integer.java:832)
at org.apache.poi.hssf.usermodel.HSSFSheet.getRow(HSSFSheet.java:385)
at org.apache.poi.hssf.usermodel.HSSFSheet.setPropertiesFromSheet(HSSFSheet.java:212)
at org.apache.poi.hssf.usermodel.HSSFSheet.<init>(HSSFSheet.java:145)
at org.apache.poi.hssf.usermodel.HSSFWorkbook.<init>(HSSFWorkbook.java:353)
at org.apache.poi.hssf.usermodel.HSSFWorkbook.<init>(HSSFWorkbook.java:398)
at org.apache.poi.hssf.usermodel.HSSFWorkbook.<init>(HSSFWorkbook.java:379)
at blog.excel.EventExample.main(EventExample.java:109)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

就报出OOM,虽然文件只有8M并且heap有100m但是还是发生了OOM,使用jvisualvm查看dump的内容



可以看到创建了许多poi内部的Record(由于我excel内都是数字所以都是NumberRecord)。

使用SAX流式读取

POI提供了SAX流式读取的方式,可以减小内存的使用

//需要继承HSSFListener
public class EventExample implements HSSFListener
{
private SSTRecord sstrec; /**
* 实现接口方法用于处理每一条记录,包括workbook/row/cell
*/
@Override
public void processRecord(Record record)
{
switch (record.getSid())
{
// 标记workbook或sheet开始,这里会进行判断
case BOFRecord.sid:
BOFRecord bof = (BOFRecord) record;
if (bof.getType() == bof.TYPE_WORKBOOK)
{
System.out.println("Encountered workbook");
// assigned to the class level member
} else if (bof.getType() == bof.TYPE_WORKSHEET)
{
System.out.println("Encountered sheet reference");
}
break;
//处理sheet
case BoundSheetRecord.sid:
BoundSheetRecord bsr = (BoundSheetRecord) record;
System.out.println("New sheet named: " + bsr.getSheetname());
break;
//处理行
case RowRecord.sid:
RowRecord rowrec = (RowRecord) record;
System.out.println("Row found, first column at "
+ rowrec.getFirstCol() + " last column at " + rowrec.getLastCol());
break;
//处理数字单元格
case NumberRecord.sid:
NumberRecord numrec = (NumberRecord) record;
System.out.println("Cell found with value " + numrec.getValue()
+ " at row " + numrec.getRow() + " and column " + numrec.getColumn());
break;
// 包含一行中所有文本单元格
case SSTRecord.sid:
sstrec = (SSTRecord) record;
for (int k = 0; k < sstrec.getNumUniqueStrings(); k++)
{
System.out.println("String table value " + k + " = " + sstrec.getString(k));
}
break;
//处理文本单元格
case LabelSSTRecord.sid:
LabelSSTRecord lrec = (LabelSSTRecord) record;
System.out.println("String cell found with value "
+ sstrec.getString(lrec.getSSTIndex()));
break;
}
} public static void main(String[] args) throws IOException
{
// 文件inputStream
FileInputStream is = new FileInputStream("d://large.xls");
POIFSFileSystem poifs = new POIFSFileSystem(is);
InputStream din = poifs.createDocumentInputStream("Workbook");
HSSFRequest req = new HSSFRequest();
// 为HSSFRequest增加listener
req.addListenerForAllRecords(new EventExample());
HSSFEventFactory factory = new HSSFEventFactory();
// 处理inputstream
factory.processEvents(req, din);
// 关闭inputstream
is.close();
din.close();
System.out.println("done.");
}
}

不足

使用以上的方法可以读取大文件,而不用担心oom发生。但是只能读到其单元格字面值,HSSF中的样式等都无法获取,而且只能进行读取无法修改、添加,这大大限制了其使用的范围。如果需要进行大文件的追加就需要后面介绍的SXSSF进行处理了。

POI-处理大Excel文件(xls)的更多相关文章

  1. POI读取/写入Excel文件

    import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io ...

  2. Apache POI 实现对 Excel 文件读写

    1. Apache POI 简介 Apache POI是Apache软件基金会的开放源码函式库. 提供API给Java应用程序对Microsoft Office格式档案读和写的功能. 老外起名字总是很 ...

  3. java中使用poi导入导出excel文件_并自定义日期格式

    Apache POI项目的使命是创造和保持java API操纵各种文件格式基于Office Open XML标准(OOXML)和微软的OLE复合文档格式(OLE2)2.总之,你可以读写Excel文件使 ...

  4. 使用(POI)SAX处理Excel文件,防止内存溢出

    POISAXReader h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-chi ...

  5. java读取excel文件(.xls,xlsx,csv)

    前提,maven工程通过poi读写excel文件,需要在pom.xml中配置依赖关系: 在<dependencies>中添加如下代码 <dependency> <grou ...

  6. Java入门开发POI读取导入Excel文件

    Apache POI是Apache开发的开源的跨平台的 Java API,提供API给Java程序对Microsoft Office格式档案进行各种操作. POI中Excel操作很简单,主要类有 HS ...

  7. C# 操作 Excel 文件(.xls 或 .xlsx)

    在.net中,常用的操作excel文件的方式,有三种: OLE DB的形式, 第三方框架NPOI, Office组件. 总结: 通过对比,在读取大数据量的excel文件,建议用OLE DB的形式,把e ...

  8. 关于NPOI导出excel文件(xls和xlsx两种格式)提示格式不符的问题

    这两天在做导出excel文件的时候遇到这个问题 本来我导出的格式是xlsx格式的,但是下载得到的文件格式变成了xls, 一开始以为是返回的contenttype设置错了 return File(ms, ...

  9. 使用poi实现生成excel文件

    import java.util.ArrayList; import javax.servlet.ServletOutputStream; import org.apache.poi.hssf.use ...

随机推荐

  1. 轻量级sqlite是增删改查

    --创建数据库 create database ios --使用数据库 use ios --创建数据表 create table student ( stuid int primary key aut ...

  2. Mysql安装脚本

    Mysql PS:要先看懂脚本在复制粘贴运行脚本,每个人的环境不一样 #/bin/bash #--------变量 #num=` + ` now_lujing=`pwd` #------------- ...

  3. (简单) POJ 3268 Silver Cow Party,Dijkstra。

    Description One cow from each of N farms (1 ≤ N ≤ 1000) conveniently numbered 1..N is going to atten ...

  4. [即时通讯]openfire启动失败解决办法

    当你发现你的电脑上的openfire无论你是重新安装还是重启都无法启动的情况下你可以按照我下面写的那些终端指令来启动你的openfire不用再去重新做你的系统了. 一般你发现你的openfire打开出 ...

  5. oracle查看表空间物理文件的名称,路径及大小

    select tablespace_name, file_id,file_name,round(bytes/(1024*1024),0) total_space from dba_data_files ...

  6. Laravel 用户验证Auth::attempt fail的问题

    1.在laravel项目中,当使用Auth::attempt()用于用户验证时,Auth::attempt()会把密码通过Hash进行转换,变成一串不知啥的长字符,如果你在数据库里事先设置了明文的密码 ...

  7. INC函数

    1.inc(integer) inc(a)等于a:=a+1; 例: i := 100; inc(i); 结果:i等于101 2.inc(integer,integer) inc(a,b)等于a:=a+ ...

  8. IOS开发-OC学习-protocol(协议)

    在OC语言中,协议是一组方法,里面有两种方法,一种是遵守这个协议的类的实例必须实现的方法,另一种是可以实现也可以不实现的方法. 例如我定义一个学生的协议,这个协议里有两个方法,其中一个是必选的方法:学 ...

  9. sql 里的 IF ELSE 语句用法

      IF ELSE 语句 IF ELSE 是最基本的编程语句结构之一几乎每一种编程语言都支持这种结构而 它在用于对从数据库返回的数据进行检查是非常有用的TRANSACT-SQL 使用IF ELSE 的 ...

  10. Node.js timer的优化故事

    前几天nodejs发布了新版本4.0,其中涉及到一个更新比较多的模块,那就是下面要介绍的timer模块. timers: Improved timer performance from porting ...