java file nio directorystream

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



(8)

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

Saludos.


Compruebe FileVisitor , muy limpio.

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


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

final List<Path> 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(); }


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<Path> stream = Files.newDirectoryStream(path)) { for (Path entry : stream) { if (Files.isDirectory(entry)) { listFiles(entry); } files.add(entry); } } }


Intenta esto ... atraviesa todas las carpetas e imprime tanto carpetas como archivos: -

public static void traverseDir(Path path) { try (DirectoryStream<Path> 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<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; } }


Java 8 proporciona una buena manera para eso:

Files.walk(path)

Este método devuelve Stream<Path> .


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

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; }


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

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 <ruta de acceso> 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 programació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<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() )); } }

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 bloqueo Obtener

Debido a que la función recursiva devuelve un Observable <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

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

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;