我的任务之一是只使用 Java 8
如果需要,则仅 Spring
,基本上对这个项目有最小的依赖。
有一个可以增长的 30 MB 大小的 excel。这每天都会通过 FTP 出现,需要处理各个行,然后需要通过电子邮件发送最终报告。
现在我的问题是文件太大,请建议最好的方法来处理它而不会出现任何内存不足错误。
谢谢
最佳答案
读取文件行的标准方法是在内存中——Guava 和 Apache Commons IO 都提供了一种快速的方法来做到这一点:
Files.readLines(new File(path), Charsets.UTF_8);
FileUtils.readLines(new File(path));
这种方法的问题是所有文件行都保存在内存中——如果文件足够大,这将很快导致 OutOfMemoryError。
For example – reading a ~1Gb file:
@Test
public void givenUsingGuava_whenIteratingAFile_thenWorks() throws IOException {
String path = ...
Files.readLines(new File(path), Charsets.UTF_8);
}
这从消耗少量内存开始:(消耗约 0 Mb)
[main] INFO org.baeldung.java.CoreJavaIoUnitTest - Total Memory: 128 Mb
[main] INFO org.baeldung.java.CoreJavaIoUnitTest - Free Memory: 116 Mb
但是,在处理完完整文件后,我们最后得到:(消耗了约 2 Gb)
[main] INFO org.baeldung.java.CoreJavaIoUnitTest - Total Memory: 2666 Mb
[main] INFO org.baeldung.java.CoreJavaIoUnitTest - Free Memory: 490 Mb
这意味着该进程消耗了大约 2.1 Gb 的内存——原因很简单——文件的行现在都存储在内存中。
此时应该很明显,将文件的内容保存在内存中将很快耗尽可用内存——不管实际有多少。
更重要的是,我们通常不需要一次将文件中的所有行都放在内存中——相反,我们只需要能够遍历每一行,进行一些处理并将其丢弃即可。所以,这正是我们要做的——在不保存内存的情况下遍历行。
使用 Apache Commons IO 进行流式传输
通过使用库提供的自定义 LineIterator 也可以使用 Commons IO 库来实现相同的目的:
LineIterator it = FileUtils.lineIterator(theFile, "UTF-8");
try {
while (it.hasNext()) {
String line = it.nextLine();
// do something with line
}
} finally {
LineIterator.closeQuietly(it);
}
Since the entire file is not fully in memory – this will also result in pretty conservative memory consumption numbers: (~150 Mb consumed)
[main] INFO o.b.java.CoreJavaIoIntegrationTest - Total Memory: 752 Mb
[main] INFO o.b.java.CoreJavaIoIntegrationTest - Free Memory: 564 Mb
关于Java 8 和文件处理 30 MB excel,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37878842/