从文件夹中读取 Java 流中的块中的文件名

Posted

技术标签:

【中文标题】从文件夹中读取 Java 流中的块中的文件名【英文标题】:Read filenames in chunks in Java streams from a folder 【发布时间】:2019-04-29 02:27:25 【问题描述】:

我有一个目录,里面有数百万个文件。我想将文件名读入 ArrayList。如果我将所有文件名读入一个 ArrayList,它会消耗更多内存。我怀疑如果目录中存在大量文件,Java 可能会抛出堆空间错误。 有没有办法分块/批量读取目录中的文件,每次说5个文件名。

【问题讨论】:

我不确定,因为我没有包含数百万个文件的目录。但也许你可以使用流?例如 Arrays.stream(Paths.get("path/to/file").toFile().list());你可以使用 StringStream 来做你想做的事情与 fileNames 即使我使用像 Arrays.stream(Paths.get("path/to/file").toFile().list()); 这样的代码,这部分Paths.get("path/to/file").toFile().list() 仍然会将所有文件名带入内存。我的问题是使用更少的内存来获取所有文件名。 【参考方案1】:

您可以为此使用Path.list,它会返回一个惰性求值的流:

List<String> fileNames = Path.list("path_to_directory")
                             .map(Path::getFileName)
                             .collect(Collectors.toList());

文件将被一一处理,这将消耗更少的内存。但是,如果最终列表 fileNames 变得太大,您仍然可能会遇到内存问题。因此流管道中的终端(收集)操作可能会导致一些问题。

但是例如,如果您直接在流上处理文件名(例如使用 forEach 而不收集它们),您可以避免将所有名称加载到内存中。

Path.list("path_to_directory")
    .map(Path::getFileName)
    .forEach(System.out::println); 

// 一个一个地打印文件,而不是同时加载所有文件。

我希望这会有所帮助。

【讨论】:

这也会将所有文件名加载到 fileNames 中。我只需要内存中文件夹中的文件名子集。假设一个文件夹中有 100 个文件,我只需要内存中的 5 个文件。打印完 5 个文件名后,我应该得到接下来的 5 个文件名并打印它们。 目标是通过不在内存中加载所有文件名来使用更少的内存 @Nishanth 如果您不使用 collect,但打印文件名目录,您可以避免将它们全部加载到内存中。请参阅我的答案的第二部分。您到底需要对文件名做什么?打印出来? 不,Anton Balaniuc 先生,我需要从文件夹中流式传输大量文件名。如果我一个一个地获取文件名,它会消耗更多时间。以块的形式获取文件名会更快。例如: f 一个文件夹有100个文件,如果我一个一个打印,它会消耗更多的时间。而是一次打印 10 个文件名将消耗更少的时间。除了使用“forEach”之外,还有什么可能的处理方式,比如“.forEachChunkOfSize(10)”? @Nishanth,流没有这种可能性。本质上,流处理元素一个一个。但您可以并行执行此操作:Path.list("path_to_directory").parallel() 会将流转换为 parallelStream【参考方案2】:

您可以使用FileVisitor 类一次遍历和读取一个文件。这样你就不会得到OOM错误。

使用Files.html#walkFileTree方法访问目录下的文件。

一般示例如下。

Path path = FileSystems.getDefault().getPath("D:\\path\\with\\lots\\of\\files");
        Files.walkFileTree(path, new FileVisitor<Path>() 
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException 
                return FileVisitResult.CONTINUE;
            

        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException 
            // here you have the files to process
            System.out.println(file);
            return FileVisitResult.CONTINUE;
        

        @Override
        public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException 
           return FileVisitResult.TERMINATE;
        

        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException 
          return FileVisitResult.CONTINUE;
        
    );

这里是Java SE tutorial 的链接。 更多示例,请参考here。

【讨论】:

以上是关于从文件夹中读取 Java 流中的块中的文件名的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Python 中的文件/流中懒惰地读取多个 JSON 值?

Java中利用IO流中的输入流读取文件实现登录功能

如何从 DryWetMidi 库中的特定 MIDI 文件块中获取程序/乐器标题?

正确读取 .wav 文件中的样本

无法使用 MappedByteBuffer 读取块中的文件

如何在从 NodeJS 中的多个输入流中读取时写入单个文件