txt resource read files java nio java-8 java-stream

resource - txt to string java 8



Files.walk(), calcular el tamaƱo total (4)

Estoy tratando de calcular el tamaño de los archivos en mi disco. En java-7 esto podría hacerse usando Files.walkFileTree como se muestra en mi respuesta here .

Sin embargo, si quisiera hacer esto utilizando java-8 streams, funcionará para algunas carpetas, pero no para todas.

public static void main(String[] args) throws IOException { long size = Files.walk(Paths.get("c:/")).mapToLong(MyMain::count).sum(); System.out.println("size=" + size); } static long count(Path path) { try { return Files.size(path); } catch (IOException | UncheckedIOException e) { return 0; } }

El código anterior funcionará bien para la ruta a:/files/ pero para c:/ se lanzará debajo de la excepción

Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException: c:/$Recycle.Bin/S-1-5-20 at java.nio.file.FileTreeIterator.fetchNextIfNeeded(Unknown Source) at java.nio.file.FileTreeIterator.hasNext(Unknown Source) at java.util.Iterator.forEachRemaining(Unknown Source) at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Unknown Source) at java.util.stream.AbstractPipeline.copyInto(Unknown Source) at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source) at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(Unknown Source) at java.util.stream.AbstractPipeline.evaluate(Unknown Source) at java.util.stream.LongPipeline.reduce(Unknown Source) at java.util.stream.LongPipeline.sum(Unknown Source) at MyMain.main(MyMain.java:16)

Entiendo de dónde viene y cómo evitarlo utilizando la API Files.walkFileTree.

Pero, ¿cómo se puede evitar esta excepción utilizando la API Files.walk() ?


2017 para los que siguen llegando aquí.

Use Files.walk () cuando esté seguro del comportamiento del sistema de archivos y realmente desee detenerse cuando haya algún error. En general, Files.walk no es útil en aplicaciones independientes. Cometo este error muy a menudo, quizás sea perezoso. Me doy cuenta de mi error en el momento en que veo que el tiempo que dura más de unos pocos segundos para algo pequeño como 1 millón de archivos.

Recomiendo walkFileTree . Comience por implementar la interfaz de FileVisitor, aquí solo quiero contar los archivos. Mal nombre de clase, lo sé.

class Recurse implements FileVisitor<Path>{ private long filesCount; @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { //This is where I need my logic filesCount++; return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { // This is important to note. Test this behaviour return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { return FileVisitResult.CONTINUE; } public long getFilesCount() { return filesCount; } }

Luego usa tu clase definida de esta manera.

Recurse r = new Recurse(); Files.walkFileTree(Paths.get("G:"), r); System.out.println("Total files: " + r.getFilesCount());

Estoy seguro de que sabe cómo modificar la implementación de sus propias clases de la clase de Interfaz FileVisitor<Path> para hacer otras cosas como el filesize con el ejemplo que publiqué. Consulte los documentos para otros métodos en este

Velocidad:

  • Files.walk: 20+ minutos y fallando con excepción
  • Files.walkFileTree: 5.6 segundos, hecho con una respuesta perfecta.

Edición: Como con todo, use pruebas para confirmar el comportamiento Manejar excepciones, todavía ocurren, excepto las que decidimos no preocuparnos por lo anterior.


Encontré que usar la clase de Archivos de Guava resolvió el problema por mí:

Iterable<File> files = Files.fileTreeTraverser().breadthFirstTraversal(dir); long size = toStream( files ).mapToLong( File::length ).sum();

Donde toStream es mi función de utilidad estática para convertir un Iterable en un Stream. Sólo esta:

StreamSupport.stream(iterable.spliterator(), false);


La respuesta corta es que no puedes.

La excepción proviene de FileTreeWalker.visit .

Para ser precisos, está tratando de construir un newDirectoryStream cuando falla (este código está fuera de su control):

// file is a directory, attempt to open it DirectoryStream<Path> stream = null; try { stream = Files.newDirectoryStream(entry); } catch (IOException ioe) { return new Event(EventType.ENTRY, entry, ioe); // ==> Culprit <== } catch (SecurityException se) { if (ignoreSecurityException) return null; throw se; }

Tal vez deberías enviar un error .


No, esta excepción no puede ser evitada.

La excepción en sí misma se produce dentro de la recuperación perezosa de Files.walk() , por lo tanto, por qué no la ves temprano y por qué no hay forma de evitarla, considera el siguiente código:

long size = Files.walk(Paths.get("C://")) .peek(System.out::println) .mapToLong(this::count) .sum();

En mi sistema esto se imprimirá en mi computadora:

C:/ C:/$Recycle.Bin Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException: C:/$Recycle.Bin/S-1-5-18

Y como excepción se lanza en el hilo (principal) del tercer archivo, todas las ejecuciones posteriores en ese hilo se detienen.

Creo que esto es un error de diseño, porque tal como se encuentra ahora en Files.walk es absolutamente inutilizable, porque nunca se puede garantizar que no haya errores al caminar sobre un directorio.

Un punto importante a tener en cuenta es que stacktrace incluye una operación sum() y reduce() , esto se debe a que la ruta se está cargando de manera perezosa, por lo que en el punto de reduce() , se llama a la mayor parte de la maquinaria de flujo (visible en stacktrace ), y luego recupera la ruta, momento en el que se produce la UnCheckedIOException .

Posiblemente podría ser evitado si deja que cada operación de caminata se ejecute en su propio hilo. Pero eso no es algo que quieras hacer de todos modos.

Además, verificar si un archivo es realmente accesible no vale nada (aunque es útil en cierta medida), porque no se puede garantizar que sea legible incluso 1 ms después.

Extensión futura

Creo que todavía se puede arreglar, aunque no sé cómo FileVisitOption exactamente FileVisitOption .
Actualmente hay un FileVisitOption.FOLLOW_LINKS , si funciona por archivo, entonces sospecho que también se podría FileVisitOption.IGNORE_ON_IOEXCEPTION un FileVisitOption.IGNORE_ON_IOEXCEPTION , sin embargo, no podemos inyectar correctamente esa funcionalidad allí.