在使用POI读取excel文件(xlsx)时发生java.lang.outofmemoryexception
我正在开发一个从Excel文件(xlsx)读取数据的Web应用程序。 我正在使用POI阅读Excel表格。 问题是当我尝试读取excel文件时,服务器抛出以下错误:
我正在尝试读取的Excel文件大小几乎为80 MB。 任何解决这个问题?
其实用户正在上传文件和应用程序后保存文件到磁盘尝试读取文件。 我用于testing的代码片段是:
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(); }
有一点会有所不同的是打开文件开始。 如果你有一个文件,然后通过它! 使用InputStream需要将所有内容caching到内存中,这就占用了空间。 既然你不需要做那个缓冲,不要!
如果您正在使用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还支持更低级别的文件处理方式。 这种较低级的方式很难使用,因为你周围没有需要整个文件的帮助器。 但是,在以stream方式处理文件时,它的内存效率要高得多。 要开始使用,请参阅POI网站上的XSSF和SAX(事件API)操作方法部分 。 试试看,也看看不同的例子。
你应该改变你的JVM的设置。 尝试将-Xmx1024 -Xms1024
添加到启动器。
你可以尝试增加你的Java堆大小。
我认为你必须增加堆的大小。 你可以通过编辑catalina.bat文件来完成。 将-Xms1024m -Xmx1024m
添加到CATALINA_OPTS
variables。
- 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.
我通过改变实施来解决问题。 实际上,首先我是从Excel文件中提取所有数据,数据是以ArrayListtypes存储的。 之后,我将数据插入数据库,这是真正的问题。 现在我根本不存储数据。 当我从ResultSet获得一条logging时,我立即将它插入到数据库中,而不是将其存储到数组列表中。 我知道这一个一个插入是不是一个好方法,但暂时我正在使用这种方法。 将来如果我find更好的,我肯定会切换到那一个。 谢谢大家。
改进你目前的方法可能是从Excel中读取大约100行(实验这个数字来获得最佳值 ),并在数据库中进行批量更新。 这将会更快。
你也可以在代码中执行一些优化,将列表创build移出外部循环(读取行数据的循环)
List data = new ArrayList();
读取string缓冲区(可能使用“逗号”分隔)中所有单元格的内容,然后将其添加到数组列表“data”
您正在向XSSFRow
添加一个XSSFRow
types的对象。 存储excel单元的整个对象没有意义。 拿出它的内容并丢弃对象。
稍后在将内容插入数据库之前,可以拆分分隔的单元格内容并执行插入。
希望这可以帮助!
你最好将它们存储在文件中,然后尝试在数据库中加载它们。 这将避免单个插入