使用nio.file.DirectoryStreamrecursion列出目录中的所有文件;

我想列出指定目录下的所有文件和该目录下的子目录。 没有目录应该列出。

我目前的代码如下。 它不能正常工作,因为它只会列出指定目录中的文件和目录。

我该如何解决这个问题?

final List<Path> files = new ArrayList<>(); Path path = Paths.get("C:\\Users\\Danny\\Documents\\workspace\\Test\\bin\\SomeFiles"); try { DirectoryStream<Path> stream; stream = Files.newDirectoryStream(path); for (Path entry : stream) { files.add(entry); } stream.close(); } catch (IOException e) { e.printStackTrace(); } for (Path entry: files) { System.out.println(entry.toString()); } 

问候。

创build一个方法,如果下一个元素是目录,将会调用它自己

 void listFiles(Path path) throws IOException { try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) { for (Path entry : stream) { if (Files.isDirectory(entry)) { listFiles(entry); } files.add(entry); } } } 

Java 8提供了一个很好的方法:

 Files.walk(path) 

这个方法返回Stream<Path>

检查FileVisitor ,非常整洁。

  Path path= Paths.get("C:\\Users\\Danny\\Documents\\workspace\\Test\\bin\\SomeFiles"); final List<Path> files=new ArrayList<>(); try { Files.walkFileTree(path, new SimpleFileVisitor<Path>(){ @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if(!attrs.isDirectory()){ files.add(file); } return FileVisitResult.CONTINUE; } }); } catch (IOException e) { e.printStackTrace(); } 

如果你想避免让函数自己调用自己,并且有一个文件列表是一个成员variables,你可以使用一个堆栈:

 private List<Path> listFiles(Path path) throws IOException { Deque<Path> stack = new ArrayDeque<Path>(); final List<Path> files = new LinkedList<>(); stack.push(path); while (!stack.isEmpty()) { DirectoryStream<Path> stream = Files.newDirectoryStream(stack.pop()); for (Path entry : stream) { if (Files.isDirectory(entry)) { stack.push(entry); } else { files.add(entry); } } stream.close(); } return files; } 

使用Rx Java,可以在坚持使用来自JDK的DirectoryStream的同时,以多种方式解决需求。

下面的组合会给你想要的效果,我会按顺序解释它们:

方法1 。 使用flatMap()和defer()运算符的recursion方法

方法2 。 使用flatMap()和fromCallable运算符的recursion方法

注意:如果将flatMap()的用法replace为concatMap() ,则目录树导航必然以深度优先search(DFS)方式进行。 使用flatMap(),DFS效果不能保证。

方法1:使用flatMap()和defer()

  private Observable<Path> recursiveFileSystemNavigation_Using_Defer(Path dir) { return Observable.<Path>defer(() -> { // // try-resource block // try(DirectoryStream<Path> children = Files.newDirectoryStream(dir)) { //This intermediate storage is required because DirectoryStream can't be navigated more than once. List<Path> subfolders = Observable.<Path>fromIterable(children) .toList() .blockingGet(); return Observable.<Path>fromIterable(subfolders) /* Line X */ .flatMap(p -> !isFolder(p) ? Observable.<Path> just(p) : recursiveFileSystemNavigation_Using_Defer(p), Runtime.getRuntime().availableProcessors()); // /* Line Y */ .concatMap(p -> !isFolder(p) ? Observable.<Path> just(p) : recursiveFileSystemNavigation_Using_Defer(p)); } catch (IOException e) { /* This catch block is required even though DirectoryStream is Closeable resource. Reason is that .close() call on a DirectoryStream throws a checked exception. */ return Observable.<Path>empty(); } }); } 

这种方法是find给定目录的孩子,然后发射孩子作为Observables。 如果一个孩子是一个文件,它将立即可用于一个订阅者,否则在X行的 flatMap()将调用recursion地传递每个子目录作为参数的方法。 对于每个这样的subdir,flatmap将在内部全部同时订阅他们的子元素。 这就像一个需要控制的连锁反应。

因此,使用Runtime.getRuntime()。availableProcessors()可以设置flatmap()的最大并发级别 ,并防止它同时订阅所有子文件夹。 没有设置并发级别,想象一下当一个文件夹有1000个孩子时会发生什么。

使用defer()可以防止过早地创buildDirectoryStream,并确保只有在真正的订阅find它的子文件夹时才会发生。

最后,这个方法返回一个Observable <Path>,以便客户端可以订阅和执行一些有用的结果,如下所示:

 // // Using the defer() based approach // recursiveDirNavigation.recursiveFileSystemNavigation_Using_Defer(startingDir) .subscribeOn(Schedulers.io()) .observeOn(Schedulers.from(Executors.newFixedThreadPool(1))) .subscribe(p -> System.out.println(p.toUri())); 

使用defer()的缺点是,如果它的参数函数抛出一个检查的exception,它不会很好地处理检查的exception。 因此,即使DirectoryStream(实现Closeable)是在try-resource块中创build的,我们仍然需要捕获IOException,因为DirectoryStream的自动closures引发了检查的exception。

在使用基于Rx的风格时,使用catch()块进行error handling听起来有点奇怪,因为即使在响应式编程中,错误也是作为事件发送的。 那么我们为什么不使用暴露这样的错误的操作符作为事件。

Rx Java 2.x中添加了一个名为fromCallable()的更好的select。 第二种方法显示了它的使用。

方法2.使用flatMap()和fromCallable运算符

此方法使用fromCallable()运算符,该运算符将Callable作为参数。 由于我们需要recursion方法,因此可调用的预期结果是给定文件夹的子元素的可观察结果。 由于我们希望订阅者在可用时收到结果,因此我们需要从此方法返回一个Observable。 由于内部可调用的结果是儿童的可观察列表,所以净效应是可观察量的可观察。

  private Observable<Observable<Path>> recursiveFileSystemNavigation_WithoutExplicitCatchBlock_UsingFromCallable(Path dir) { /* * fromCallable() takes a Callable argument. In this case the callbale's return value itself is * a list of sub-paths therefore the overall return value of this method is Observable<Observable<Path>> * * While subscribing the final results, we'd flatten this return value. * * Benefit of using fromCallable() is that it elegantly catches the checked exceptions thrown * during the callable's call and exposes that via onError() operator chain if you need. * * Defer() operator does not give that flexibility and you have to explicitly catch and handle appropriately. */ return Observable.<Observable<Path>> fromCallable(() -> traverse(dir)) .onErrorReturnItem(Observable.<Path>empty()); } private Observable<Path> traverse(Path dir) throws IOException { // // try-resource block // try(DirectoryStream<Path> children = Files.newDirectoryStream(dir)) { //This intermediate storage is required because DirectoryStream can't be navigated more than once. List<Path> subfolders = Observable.<Path>fromIterable(children) .toList() .blockingGet(); return Observable.<Path>fromIterable(subfolders) /* Line X */ .flatMap(p -> ( !isFolder(p) ? Observable.<Path> just(p) : recursiveFileSystemNavigation_WithoutExplicitCatchBlock_UsingFromCallable(p).blockingSingle()) ,Runtime.getRuntime().availableProcessors()); // /* Line Y */ .concatMap(p -> ( !isFolder(p) ? Observable.<Path> just(p) : recursiveFileSystemNavigation_WithoutExplicitCatchBlock_UsingFromCallable(p).blockingSingle() )); } } 

用户需要将结果stream展平,如下所示:

 // // Using the fromCallable() based approach // recursiveDirNavigation.recursiveFileSystemNavigation_WithoutExplicitCatchBlock_UsingFromCallable(startingDir) .subscribeOn(Schedulers.io()) .flatMap(p -> p) .observeOn(Schedulers.from(Executors.newFixedThreadPool(1))) .subscribe(filePath -> System.out.println(filePath.toUri())); 

在traverse()方法中,为什么行X使用阻塞Get

由于recursion函数返回一个Observable <Observable>,但该行的flatmap需要一个Observable来订阅。

这两种方法中的Y行使用concatMap()

因为concatMap()可以很好的使用,如果我们不想在flatmap()所做的innner订阅期间进行并行处理。

在这两种方法中,方法isFolder的实现如下所示:

 private boolean isFolder(Path p){ if(p.toFile().isFile()){ return false; } return true; } 

Java RX 2.0的Maven坐标

 <dependency> <groupId>io.reactivex.rxjava2</groupId> <artifactId>rxjava</artifactId> <version>2.0.3</version> </dependency> 

在Java文件中导入

 import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.concurrent.Executors; import io.reactivex.Observable; import io.reactivex.schedulers.Schedulers; 

试试这个..它遍历每个文件夹并打印文件夹以及文件:

 public static void traverseDir(Path path) { try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) { for (Path entry : stream) { if (entry.toFile().isDirectory()) { System.out.println("Sub-Folder Name : " + entry.toString()); traverseDir(entry); } else { System.out.println("\tFile Name : " + entry.toString()); } } } catch (IOException e) { e.printStackTrace(); } } 

试试:你会得到一个目录和子目录path的列表; 可能有无限的子目录,尝试使用recursive过程。

 public class DriectoryFileFilter { private List<String> filePathList = new ArrayList<String>(); public List<String> read(File file) { if (file.isFile()) { filePathList.add(file.getAbsolutePath()); } else if (file.isDirectory()) { File[] listOfFiles = file.listFiles(); if (listOfFiles != null) { for (int i = 0; i < listOfFiles.length; i++){ read(listOfFiles[i]); } } else { System.out.println("[ACCESS DENIED]"); } } return filePathList; } }