Java.nio: directorio recursivo más conciso eliminar


Actualmente estoy intentando eliminar recursivamente un directorio... Curiosamente, la pieza más corta de código que pude encontrar es la siguiente construcción, empleando una clase interna ad-hoc y en un patrón de visitante ...

Path rootPath = Paths.get("data/to-delete");

try {
  Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
      System.out.println("delete file: " + file.toString());
      Files.delete(file);
      return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
      Files.delete(dir);
      System.out.println("delete dir: " + dir.toString());
      return FileVisitResult.CONTINUE;
    }
  });
} catch(IOException e){
  e.printStackTrace();
}

Fuente: aquí

Esto se siente terriblemente torpe y prolijo, dado que las nuevas API nio eliminan tanto desorden y repetición...

¿Hay alguna forma más corta de lograr un directorio recursivo forzado ¿borrar?

Estoy buscando métodos nativos puros de Java 1.8, así que por favor no enlace a bibliotecas externas...

Author: fgysin, 2016-03-14

5 answers

Puede combinar NIO 2 y la API de Stream.

Path rootPath = Paths.get("/data/to-delete");
// before you copy and paste the snippet
// - read the post till the end
// - read the javadoc to understand what the code will do 
//
// a) to follow softlinks (removes the linked file too) use
// Files.walk(rootPath, FileVisitOption.FOLLOW_LINKS)
//
// b) to not follow softlinks (removes only the softlink) use
// the snippet below
Files.walk(rootPath)
    .sorted(Comparator.reverseOrder())
    .map(Path::toFile)
    .peek(System.out::println)
    .forEach(File::delete);
  • Files.walk - devuelve todos los archivos/directorios debajo de rootPath incluyendo
  • .sorted - ordenar la lista en orden inverso, por lo que el directorio en sí viene después de los subdirectorios y archivos que incluyen
  • .map - mapear el Path a File
  • .peek - ¿existe solo para mostrar qué entrada se procesa
  • .forEach - llama al método .delete() en cada objeto File

EDITAR

Aquí hay algunas cifras.
El directorio /data/to-delete contenía el descomprimido rt.jarde jdk1.8.0_73 y una reciente compilación de activemq.

files: 36,427
dirs :  4,143
size : 514 MB

Veces en milisegundos

                    int. SSD     ext. USB3
NIO + Stream API    1,126        11,943
FileVisitor         1,362        13,561

Ambas versiones se ejecutaron sin imprimir nombres de archivo. El factor más limitante es la unidad. No la implementación.

EDITAR

Alguna información adicional sobre la opción FileVisitOption.FOLLOW_LINKS.

Asumir el siguiente archivo y directorio estructura

/data/dont-delete/bar
/data/to-delete/foo
/data/to-delete/dont-delete -> ../dont-delete

Usando

Files.walk(rootPath, FileVisitOption.FOLLOW_LINKS)

Seguirá los enlaces simbólicos y el archivo /tmp/dont_delete/bar también se eliminará.

Usando

Files.walk(rootPath)

No seguirá enlaces simbólicos y el archivo /tmp/dont_delete/bar no se eliminará.

NOTA: Nunca use código como copiar y pegar sin entender lo que hace.

 86
Author: SubOptimal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-22 05:58:56

La siguiente solución no necesita la conversión de los objetos Path a File:

Path rootPath = Paths.get("/data/to-delete");     
final List<Path> pathsToDelete = Files.walk(rootPath).sorted(Comparator.reverseOrder()).collect(Collectors.toList());
for(Path path : pathsToDelete) {
    Files.deleteIfExists(path);
}
 4
Author: asmaier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-27 15:42:58

Si solo debe usar Java 7 con NIO

Path path = Paths.get("./target/logs");
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
  @Override
  public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
    Files.delete(file);
    return FileVisitResult.CONTINUE;
  }

  @Override
  public FileVisitResult postVisitDirectory(Path dir, IOException exc)
      throws IOException {
    Files.delete(dir);
    return FileVisitResult.CONTINUE;
  }
});
 2
Author: bigspawn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-23 11:36:54

Si ya tienes Spring Core como parte de tu proyecto, aquí tienes una manera fácil de hacerlo:

FileSystemUtils.deleteRecursively(file);

Fuente:http://www.baeldung.com/java-delete-directory

 1
Author: justanother,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-13 15:47:51
Files.walk(pathToBeDeleted)
  .sorted(Comparator.reverseOrder())
  .map(Path::toFile)
  .forEach(File::delete);

Fuente: http://www.baeldung.com/java-delete-directory (Usando NIO2 con Java 8).

También, probablemente un comentario no deseado, pero sería mucho más limpio y legible usar una biblioteca. Con el código en una función compartida, no ocupará mucho espacio. Cada persona que mira su código debe validar que este código hace una eliminación adecuada, y de ninguna manera es obvio.

 0
Author: user1122069,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-22 23:26:33