Enumera recursivamente todos los archivos dentro de un directorio usando nio.file.DirectoryStream;

Quiero enumerar todos los ARCHIVOS dentro del directorio y subdirectorios especificados dentro de ese directorio. No se deben enumerar directorios.

Mi código actual está abajo. No funciona correctamente, ya que solo enumera los archivos y directorios dentro del directorio especificado.

¿Cómo puedo arreglar esto?

final List files = new ArrayList(); Path path = Paths.get("C:\\Users\\Danny\\Documents\\workspace\\Test\\bin\\SomeFiles"); try { DirectoryStream 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()); } 

Saludos.

Java 8 proporciona una buena manera para eso:

 Files.walk(path) 

Este método devuelve Stream .

Haz un método que se llamará a sí mismo si el siguiente elemento es el directorio

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

Compruebe FileVisitor , muy limpio.

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

Si desea evitar que la función se llame recursivamente y tenga una lista de archivos que sea una variable miembro, puede usar una stack:

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

Usando Rx Java, el requisito se puede resolver de varias maneras, al mismo tiempo que se mantiene el uso de DirectoryStream desde JDK.

Las siguientes combinaciones le darán el efecto deseado, las explicaría en secuencia:

Enfoque 1 . Un enfoque recursivo utilizando los operadores flatMap () y defer ()

Enfoque 2 . Un enfoque recursivo usando flatMap () y operadores de CallCable

Nota: Si reemplaza el uso de flatMap () con concatMap () , la navegación en el árbol del directorio necesariamente se realizará en modo de búsqueda en profundidad (DFS). Con flatMap (), el efecto DFS no está garantizado.

Método 1: usar flatMap () y diferir ()

  private Observable recursiveFileSystemNavigation_Using_Defer(Path dir) { return Observable.defer(() -> { // // try-resource block // try(DirectoryStream children = Files.newDirectoryStream(dir)) { //This intermediate storage is required because DirectoryStream can't be navigated more than once. List subfolders = Observable.fromIterable(children) .toList() .blockingGet(); return Observable.fromIterable(subfolders) /* Line X */ .flatMap(p -> !isFolder(p) ? Observable. just(p) : recursiveFileSystemNavigation_Using_Defer(p), Runtime.getRuntime().availableProcessors()); // /* Line Y */ .concatMap(p -> !isFolder(p) ? Observable. 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.empty(); } }); } 

Este enfoque es encontrar niños de un directorio determinado y luego emitirlos como Observables. Si un niño es un archivo, estará disponible inmediatamente para un suscriptor, si no, flatMap () en la Línea X invocará el método recursivamente pasando cada subdirectorio como argumento. Para cada subdirectorio, flatmap se suscribirá internamente a sus hijos, todo al mismo tiempo. Esto es como una reacción en cadena que necesita ser controlada.

Por lo tanto, el uso de Runtime.getRuntime (). AvailableProcessors () establece el nivel máximo de concurrencia para flatmap () y evita que se suscriba a todas las subcarpetas al mismo tiempo. Sin establecer el nivel de concurrencia, imagine lo que sucederá cuando una carpeta tenga 1000 hijos.

El uso de defer () evita la creación de un DirectoryStream de forma prematura y garantiza que solo ocurrirá cuando se realice una suscripción real para encontrar sus subcarpetas.

Finalmente, el método devuelve un observable para que un cliente pueda suscribirse y hacer algo útil con los resultados, como se muestra a continuación:

 // // 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())); 

La desventaja de utilizar defer () es que no trata bien las excepciones comprobadas si su función de argumento arroja una excepción marcada. Por lo tanto, aunque DirectoryStream (que implementa Closeable) se creó en un bloque try-resource, aún tuvimos que atrapar la IOException porque el cierre automático de un DirectoryStream arroja esa excepción marcada.

Al usar el estilo basado en Rx, el uso de los bloques catch () para el manejo de errores suena un poco extraño porque incluso los errores se envían como eventos en la progtwigción reactiva. Entonces, ¿por qué no usamos un operador que expone tales errores como eventos?

Se agregó una mejor alternativa llamada fromCallable () en Rx Java 2.x. El segundo enfoque muestra el uso de la misma.

Enfoque 2. Uso de los operadores flatMap () y fromCallable

Este enfoque usa el operador de Calibración () que toma un Callable como argumento. Como queremos un enfoque recursivo, el resultado esperado de ese invocable es un Observable de hijos de una carpeta determinada. Como queremos que un suscriptor reciba resultados cuando estén disponibles, debemos devolver un Observable desde este método. Debido a que el resultado del cálculo interno es una lista observable de niños, el efecto neto es un Observable de Observables.

  private Observable> 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> * * 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.> fromCallable(() -> traverse(dir)) .onErrorReturnItem(Observable.empty()); } private Observable traverse(Path dir) throws IOException { // // try-resource block // try(DirectoryStream children = Files.newDirectoryStream(dir)) { //This intermediate storage is required because DirectoryStream can't be navigated more than once. List subfolders = Observable.fromIterable(children) .toList() .blockingGet(); return Observable.fromIterable(subfolders) /* Line X */ .flatMap(p -> ( !isFolder(p) ? Observable. just(p) : recursiveFileSystemNavigation_WithoutExplicitCatchBlock_UsingFromCallable(p).blockingSingle()) ,Runtime.getRuntime().availableProcessors()); // /* Line Y */ .concatMap(p -> ( !isFolder(p) ? Observable. just(p) : recursiveFileSystemNavigation_WithoutExplicitCatchBlock_UsingFromCallable(p).blockingSingle() )); } } 

Un suscriptor necesitará aplanar la secuencia de resultados como se muestra a continuación:

 // // 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())); 

En el método de poligonal (), ¿por qué la línea X usa el locking Obtener

Debido a que la función recursiva devuelve un Observable , pero el mapa plano en esa línea necesita un Observable para suscribirse.

La línea Y en ambos enfoques usa concatMap ()

Porque concatMap () se puede usar cómodamente si no queremos el paralelismo durante las suscripciones innner hechas por flatmap ().

En ambos enfoques, la implementación del método isFolder se ve a continuación:

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

Coordenadas de Maven para Java RX 2.0

  io.reactivex.rxjava2 rxjava 2.0.3  

Importaciones en archivo 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; 

Esta es la implementación más corta que se me ocurrió:

 final List files = new ArrayList<>(); Path path = Paths.get("C:\\Users\\Danny\\Documents\\workspace\\Test\\bin\\SomeFiles"); try { Files.walk(path).forEach(entry -> list.add(entry)); } catch (IOException e) { e.printStackTrack(); } 

Intenta esto … atraviesa todas las carpetas e imprime tanto carpetas como archivos: –

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

Intente: obtendrá una lista de la ruta del directorio y del subdirectorio; Puede haber un subdirectorio ilimitado, intente utilizar un proceso recursive .

 public class DriectoryFileFilter { private List filePathList = new ArrayList(); public List 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; } }