使用POI读取excel文件(xlsx)时出现java.lang.outofmemory异常

时间:2012-06-20 11:38:12

标签: java excel apache-poi out-of-memory

我正在开发一个从excel文件(xlsx)读取数据的Web应用程序。我正在使用POI阅读excel表。问题是当我尝试读取excel文件时,服务器抛出以下错误:

enter image description here

我试图读取的excel文件大小几乎为80 MB。解决这个问题的方法是什么?

实际上用户在将文件保存到磁盘后上传文件和应用程序尝试读取文件。 我用于测试的代码片段是:

 File savedFile = new File(file_path);

FileInputStream fis = null;
            try {

                fis = new FileInputStream(savedFile);
                XSSFWorkbook xWorkbook = new XSSFWorkbook(fis);
                XSSFSheet xSheet = xWorkbook.getSheetAt(5);

                Iterator rows = xSheet.rowIterator();
                while (rows.hasNext()) {
                    XSSFRow row = (XSSFRow) rows.next();
                    Iterator cells = row.cellIterator();

                    List data = new ArrayList();
                    while (cells.hasNext()) {
                        XSSFCell cell = (XSSFCell) cells.next();
                        System.out.println(cell.getStringCellValue());
                        data.add(cell);
                    }

                }
            } catch (IOException e) {
                e.printStackTrace();
            } 

7 个答案:

答案 0 :(得分:3)

有一点不同的是,在打开文件时会有所不同。如果你有一个文件,那么传递它!使用InputStream需要将所有内容缓冲到内存中,从而占用空间。既然你不需要做那个缓冲,那就不要!

如果您使用最新的POI夜间版本,那么它非常简单。您的代码变为:

File file = new File(file_path);
OPCPackage opcPackage = OPCPackage.open(file);
XSSFWorkbook workbook = new XSSFWorkbook(opcPackage);

否则,它非常相似:

File file = new File(file_path);
OPCPackage opcPackage = OPCPackage.open(file.getAbsolutePath());
XSSFWorkbook workbook = new XSSFWorkbook(opcPackage);

那会让你有点内存,这可能已经足够了。如果不是,并且如果您无法增加足够的Java堆空间来应对,那么您将不得不停止使用XSSF UserModel。

除了您一直使用的当前友好的UserModel,POI还支持较低级别的处理文件的方式。这种较低级别的方式更难以使用,因为您没有需要整个文件在内存中的各种帮助程序。但是,当您以流方式处理文件时,它的内存效率要高得多。要开始使用,请参阅POI网站上的XSSF and SAX (Event API) How-To section。试试这个,并看看各种例子。

答案 1 :(得分:2)

您应该更改JVM的设置。尝试将-Xmx1024 -Xms1024添加到启动器。

答案 2 :(得分:1)

您可以尝试增加Java堆大小。

答案 3 :(得分:1)

我认为你必须增加堆的大小。 您可以通过编辑catalina.bat文件来完成。将-Xms1024m -Xmx1024m添加到CATALINA_OPTS变量。

  • Xms =初始Java堆大小
  • Xmx =最大Java堆大小

编辑: 来自Catalina.bat


rem   CATALINA_OPTS   (Optional) Java runtime options used when the "start",
rem                   "run" or "debug" command is executed.
rem                   Include here and not in JAVA_OPTS all options, that should
rem                   only be used by Tomcat itself, not by the stop process,
rem                   the version command etc.
rem                   Examples are heap size, GC logging, JMX ports etc.

答案 4 :(得分:0)

我通过改变实施来解决问题。实际上我首先从Excel文件中获取所有数据,并且数据存储在ArrayList类型中。之后我将数据插入数据库,这才是真正的问题。现在我根本不存储数据。当我从ResultSet中获取一条记录时,我立即将其插入到DB中,而不是将其存储到arraylist中。我知道这个逐个插入不是一个好方法,但暂时我正在使用这种方法。将来,如果我找到更好的,我肯定会转向那个。谢谢大家。

答案 5 :(得分:0)

对当前方法的改进可能是从excel读取大约100行(试验此图以获得最佳值)并在数据库中进行批量更新。这会更快。

此外,您可以在代码中执行一些优化,将列表创建移出外部循环(用于读取行数据的循环)

  

List data = new ArrayList();

读取字符串缓冲区中一行中存在的所有单元格的内容(可能用“逗号”分隔),然后将其添加到arraylist “data”

您正在向arraylist添加XSSFRow类型的对象。存储excel单元的整个对象没有意义。取出其内容并丢弃该物体。

稍后在将内容插入数据库之前,您可以拆分分隔的单元格内容并执行插入。

希望这有帮助!

答案 6 :(得分:-1)

您最好将它们存储在文件中,然后尝试将它们加载到数据库中。 这样可以避免单次插入