web-dev-qa-db-fra.com

Java.nio: suppression récursive la plus concise du répertoire

J'essaie actuellement de supprimer récursivement un répertoire ... Curieusement, le morceau de code le plus court que j'ai pu trouver est la construction suivante, qui utilise une classe interne - ad-hoc et visitor pattern ...

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

Source: ici

Cela semble horriblement maladroit et prolixe, étant donné que les nouvelles API nio suppriment tant d'encombrement et de passe-partout ...

Existe-t-il un moyen plus rapide de supprimer de manière forcée et récursive un répertoire?

Je recherche des méthodes natives pures Java 1.8, alors veuillez ne pas créer de lien vers des bibliothèques externes ...

37
fgysin

Vous pouvez combiner NIO 2 et l’API 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 - renvoie tous les fichiers/répertoires sous rootPath, y compris
  • .sorted - trie la liste dans l'ordre inverse, le répertoire vient donc après les sous-répertoires et fichiers inclus
  • .map - mappe le Path à File
  • .peek - existe-t-il uniquement pour indiquer quelle entrée est traitée?
  • .forEach - appelle la méthode .delete() sur chaque objet File

[~ # ~] éditer [~ # ~]

Voici quelques chiffres.
Le répertoire /data/to-delete Contenait le rt.jar Non compressé de jdk1.8.0_73 et une version récente de activemq .

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

Temps en millisecondes

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

Les deux versions ont été exécutées sans impression de noms de fichiers. Le facteur le plus limitant est le lecteur. Pas la mise en œuvre.

[~ # ~] éditer [~ # ~]

Quelques informations supplémentaires sur l’option FileVisitOption.FOLLOW_LINKS.

Supposons la structure de fichiers et de répertoires suivante

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

En utilisant

Files.walk(rootPath, FileVisitOption.FOLLOW_LINKS)

suivra les liens symboliques et le fichier /tmp/dont_delete/bar sera également supprimé.

En utilisant

Files.walk(rootPath)

ne suivra pas les liens symboliques et le fichier /tmp/dont_delete/bar ne sera pas supprimé.

REMARQUE: N'utilisez jamais de code en tant que copier-coller sans comprendre ce qu'il fait.

104
SubOptimal

La solution suivante n'a pas besoin de la conversion des objets Chemin en fichier:

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);
}
5
asmaier

Si vous devez utiliser uniquement Java 7 avec 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;
  }
});
4
bigspawn

Si vous avez déjà Spring Core dans le cadre de votre projet, voici un moyen simple de le faire:

FileSystemUtils.deleteRecursively(file);

Source: http://www.baeldung.com/Java-delete-directory

3
justanother
Files.walk(pathToBeDeleted).sorted(Comparator.reverseOrder()).forEach(Files::delete);

Vous aurez besoin du modèle "essayer avec les ressources" pour fermer le flux si "l'élimination des ressources du système de fichiers en temps voulu est requise".

Aussi, probablement un commentaire importun, mais il serait beaucoup plus propre et plus lisible d’utiliser une bibliothèque. Avec le code dans une fonction partagée, cela ne prendra pas beaucoup d'espace. Toute personne qui consulte votre code doit valider que ce code effectue une suppression correcte, ce qui n’est nullement évident.

1
user1122069

FileUtils.deleteDirectory de Apache Commons IO supprime un répertoire de manière récursive.

Exemple:

Path pathToBeDeleted = TEMP_DIRECTORY.resolve(DIRECTORY_NAME);

boolean result = FileUtils.deleteDirectory(pathToBeDeleted.toFile());

Pour plus d'informations, voir Supprimer un répertoire de manière récursive en Java .

0
Fernando Correia