在 Java 中读取数万个文件并写入数百万个文件

Read tens of thousands of Files and write to millions of files in Java

我正在进行一些不寻常的数据操作。我有 36,000 个输入文件。更多然后可以一次加载到内存中。我想获取每个文件的第一个字节并将其放入一个输出文件中,然后对第二个文件再次执行此操作,依此类推。它不需要按任何特定顺序完成。因为输入文件被压缩加载它们需要更长的时间,并且它们不能一次读取 1 个字节。我最终得到了每个输入文件的字节数组。

输入文件约为 1-6MB 未压缩和 ~.3-1MB 压缩(有损压缩)。输出文件最终是输入文件的字节数。在我的示例中为 ~36KB。

我知道 ulimit 可以在 Linux OS 上设置,等效的可以在 windows 上完成。即使可以提高这个数字,我也不认为任何 OS 会喜欢同时写入数百万个文件。

我目前的解决方案是制作 3000 个左右的缓冲写入器流并依次加载每个输入文件并将 1 个字节写入 3000 个文件,然后关闭文件并加载下一个输入。使用此系统,每个输入文件都需要打开大约 500 次。

整个操作需要 8 天才能完成,这只是一个更实际的应用程序的测试用例,最终会产生更大的输入文件、更多的输入文件和更多的输出文件。

在内存中捕获所有压缩文件然后根据需要解压缩它们听起来不切实际,并且不会扩展到更大的输入文件。

我认为解决方案是从输入文件中缓冲我所能缓冲的内容(因为内存限制不允许缓冲所有内容),然后按顺序写入文件,然后重新执行。

但是我不知道是否有更好的解决方案使用我没有阅读过的东西。

编辑 感谢您的快速回复。我知道我在应用我正在做的事情时含糊不清,我会尽力纠正它。我基本上有一个三维数组 [images][X][Y] 我想遍历每个图像并保存每个图像上特定像素的每种颜色,并对所有图像执行此操作。问题是内存限制。

byte[] 像素 = ((DataBufferByte) ImageIO.read( fileList.get(k) ).getRaster().getDataBuffer()).getData();

这就是我用来加载图像的方式,因为它负责解压和跳过 header。

我不会将其编辑为视频,因为我必须先获取帧,然后将其转换为图像(成本高昂的颜色 space 转换),然后将其转换为 byte[]获取像素数据 int RGB 颜色 space。

我可以加载每张图片并将其分成约 500 个部分(Y 的大小)并写入我保持打开状态并为每张图片写入的单独文件。输出很容易在演出中。生成的文件可以完全加载到内存中,并变成一个数组用于顺序文件写入。

中间步骤确实意味着我可以在网络上分担负载,但我试图在配备 4gb 内存、没有 GPU 和低质量 i7 的低质量笔记本电脑上完成它。

在阅读 davidbak 的回复之前,我没有考虑将任何内容保存到文件中作为中间步骤。大小是唯一让这个问题变得不重要的因素,我现在看到大小可以分成更小、更易于管理的块。

三相运行:

第一阶段:读取所有输入文件,一次一个,然后写入单个输出文件。输出文件将是面向记录的——比如说,8 字节记录、4 字节 "character offset" 和 4 字节 "character codepoint"。当您读取文件时,字符偏移量当然从 0 开始,因此如果输入文件是 "ABCD" 您正在编写 (0, A) (1, B) (2, C) (3, D).每个输入文件打开一次,顺序读取并关闭。输出文件打开一次,按顺序写入,然后关闭。

第二阶段:使用外部排序将中间文件的8字节记录在4字节字符偏移字段上进行排序。

第三阶段:打开排序后的中间文件并进行一次遍历。每次字符索引字段更改时打开一个新的输出文件,并将属于该索引的所有字符写入该输出文件。输入文件打开一次并按顺序读取。打开每个输出文件,按顺序写入,然后关闭。

瞧!您需要 space 作为中间文件,并需要良好的外部排序(以及 space 作为其工作文件)。

正如@Jorge 所建议的,第 1 阶段和第 2 阶段都可以并行化,事实上,概述的这类工作(第 1 到 3 阶段)恰好在 mapreduce/hadoop 的最佳位置。

你说的很含糊,但是,也许看看 mapreduce 会有所帮助。似乎是那种可以分配的工作。

根据您提供的附加信息,我真的不知道如何在您提到的 4GB i7 等常见硬件上执行该任务。您的问题看起来像是一种图像堆叠算法,可以从很多不太好的图像中获得像样的图像,这是天文图像处理中的一个典型问题,我相信它也适用于其他领域。好好研究一下天文成像处理可能会很好地利用你的时间,有一个名为 registax 的软件(不确定它是否仍然存在)可以做类似的事情,但使用视频文件。

如果您花 1 秒打开一个文件,您只需花 1 秒钟打开文件就可以得到 10 小时的时间。

一种方法是获取一些 FAST 磁盘 (SSD),我会将所有文件解压缩为某种原始格式并将它们存储在磁盘上,从那里你将不得不使用文件指针直接读取从文件中提取文件而不将它们放入内存并将输出直接写入磁盘上的文件。