Est-il possible de supprimer des répertoires entiers de manière récursive en Java?
Dans le cas normal, il est possible de supprimer un répertoire vide. Cependant, lorsqu'il s'agit de supprimer des répertoires entiers avec du contenu, ce n'est plus aussi simple que cela.
Comment pouvez-vous supprimer des répertoires entiers avec le contenu en Java?
Vous devriez vérifier Apache's commons-io . Il a une classe FileUtils qui fera ce que vous voulez.
FileUtils.deleteDirectory(new File("directory"));
Avec Java 7, nous pouvons enfin le faire avec une détection de lien symbolique fiable. (Je ne considère pas que les biens communs d'Apache soient fiables détection des liens symboliques à ce stade, car elle ne gère pas les liens sous Windows créés avec mklink
.)
Par souci d’historique, voici une réponse antérieure à Java 7, qui suit les liens symboliques.
void delete(File f) throws IOException {
if (f.isDirectory()) {
for (File c : f.listFiles())
delete(c);
}
if (!f.delete())
throw new FileNotFoundException("Failed to delete file: " + f);
}
Dans Java 7+, vous pouvez utiliser la classe Files
. Le code est très simple:
Path directory = Paths.get("/tmp");
Files.walkFileTree(directory, 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;
}
});
Java 7 a pris en charge les répertoires nomades avec la gestion des liens symboliques:
import Java.nio.file.*;
public static void removeRecursive(Path path) throws IOException
{
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 visitFileFailed(Path file, IOException exc) throws IOException
{
// try to delete the file anyway, even if its attributes
// could not be read, since delete-only access is
// theoretically possible
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException
{
if (exc == null)
{
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
else
{
// directory iteration failed; propagate exception
throw exc;
}
}
});
}
J'utilise ceci comme solution de rechange aux méthodes spécifiques à la plate-forme (dans ce code non testé ):
public static void removeDirectory(Path directory) throws IOException
{
// does nothing if non-existent
if (Files.exists(directory))
{
try
{
// prefer OS-dependent directory removal tool
if (SystemUtils.IS_OS_WINDOWS)
Processes.execute("%ComSpec%", "/C", "RD /S /Q \"" + directory + '"');
else if (SystemUtils.IS_OS_UNIX)
Processes.execute("/bin/rm", "-rf", directory.toString());
}
catch (ProcessExecutionException | InterruptedException e)
{
// fallback to internal implementation on error
}
if (Files.exists(directory))
removeRecursive(directory);
}
}
(SystemUtils est de Apache Commons Lang . Processus est privé mais son comportement devrait être évident.)
One-Liner Solution (Java8) pour supprimer tous les fichiers et répertoires de manière récursive, y compris le répertoire de départ:
Files.walk(Paths.get("c:/dir_to_delete/"))
.map(Path::toFile)
.sorted((o1, o2) -> -o1.compareTo(o2))
.forEach(File::delete);
Nous utilisons un comparateur pour l'ordre inverse, sinon File :: delete ne pourra pas supprimer le répertoire éventuellement non vide. Donc, si vous voulez conserver des répertoires et ne supprimer que des fichiers, supprimez simplement le comparateur à sorted () o supprimez complètement le tri et ajoutez le filtre de fichiers:
Files.walk(Paths.get("c:/dir_to_delete/"))
.filter(Files::isRegularFile)
.map(Path::toFile)
.forEach(File::delete);
Je viens de voir que ma solution est plus ou moins la même que celle d’erickson, elle est simplement présentée sous forme de méthode statique. Laissez tomber cela quelque part, c'est beaucoup plus léger que d'installer tout Apache Commons pour quelque chose qui (comme vous pouvez le voir) est assez simple.
public class FileUtils {
/**
* By default File#delete fails for non-empty directories, it works like "rm".
* We need something a little more brutual - this does the equivalent of "rm -r"
* @param path Root File Path
* @return true iff the file and all sub files/directories have been removed
* @throws FileNotFoundException
*/
public static boolean deleteRecursive(File path) throws FileNotFoundException{
if (!path.exists()) throw new FileNotFoundException(path.getAbsolutePath());
boolean ret = true;
if (path.isDirectory()){
for (File f : path.listFiles()){
ret = ret && deleteRecursive(f);
}
}
return ret && path.delete();
}
}
Une solution avec une pile et sans méthodes récursives:
File dir = new File("/path/to/dir");
File[] currList;
Stack<File> stack = new Stack<File>();
stack.Push(dir);
while (! stack.isEmpty()) {
if (stack.lastElement().isDirectory()) {
currList = stack.lastElement().listFiles();
if (currList.length > 0) {
for (File curr: currList) {
stack.Push(curr);
}
} else {
stack.pop().delete();
}
} else {
stack.pop().delete();
}
}
Guava avait Files.deleteRecursively(File)
pris en charge jusqu'à Guava 9 .
De Guava 1 :
Obsolète. Cette méthode souffre d'une mauvaise détection des liens symboliques et des conditions de concurrence. Cette fonctionnalité ne peut être prise en charge de manière appropriée qu’en utilisant une commande du système d’exploitation telle que
rm -rf
oudel /s
. Il est prévu que cette méthode sera supprimée de Guava dans la version 11.0 de Guava.
Par conséquent, il n’existe pas de méthode de ce type dans Guava 11 .
Si vous avez Spring, vous pouvez utiliser FileSystemUtils.deleteRecursively :
import org.springframework.util.FileSystemUtils;
boolean success = FileSystemUtils.deleteRecursively(new File("directory"));
for(Path p : Files.walk(directoryToDelete).
sorted((a, b) -> b.compareTo(a)). // reverse; files before dirs
toArray(Path[]::new))
{
Files.delete(p);
}
Ou si vous voulez gérer le IOException
:
Files.walk(directoryToDelete).
sorted((a, b) -> b.compareTo(a)). // reverse; files before dirs
forEach(p -> {
try { Files.delete(p); }
catch(IOException e) { /* ... */ }
});
public void deleteRecursive(File path){
File[] c = path.listFiles();
System.out.println("Cleaning out folder:" + path.toString());
for (File file : c){
if (file.isDirectory()){
System.out.println("Deleting file:" + file.toString());
deleteRecursive(file);
file.delete();
} else {
file.delete();
}
}
path.delete();
}
static public void deleteDirectory(File path)
{
if (path == null)
return;
if (path.exists())
{
for(File f : path.listFiles())
{
if(f.isDirectory())
{
deleteDirectory(f);
f.delete();
}
else
{
f.delete();
}
}
path.delete();
}
}
Deux façons d'échouer avec les liens symboliques et le code ci-dessus ... sans connaître la solution.
Exécutez ceci pour créer un test:
echo test > testfile
mkdir dirtodelete
ln -s badlink dirtodelete/badlinktodelete
Ici vous voyez votre fichier de test et votre répertoire de test:
$ ls testfile dirtodelete
testfile
dirtodelete:
linktodelete
Ensuite, lancez votre commons-io deleteDirectory (). Il se bloque en disant que le fichier est introuvable. Pas sûr de ce que les autres exemples font ici. La commande Linux rm supprimerait simplement le lien, et rm -r sur le répertoire également.
Exception in thread "main" Java.io.FileNotFoundException: File does not exist: /tmp/dirtodelete/linktodelete
Exécutez ceci pour créer un test:
mkdir testdir
echo test > testdir/testfile
mkdir dirtodelete
ln -s ../testdir dirtodelete/dirlinktodelete
Ici vous voyez votre fichier de test et votre répertoire de test:
$ ls dirtodelete testdir
dirtodelete:
dirlinktodelete
testdir:
testfile
Ensuite, lancez commons-io deleteDirectory () ou les exemples de code publiés. Il supprime non seulement le répertoire, mais votre fichier de test qui est en dehors du répertoire en cours de suppression. (Il supprime implicitement le répertoire et supprime le contenu). rm -r supprimerait le lien uniquement. Vous devez utiliser quelque chose comme ceci: supprimez les fichiers annulés: "find -L dirtodelete -type f -exec rm {} \;".
$ ls dirtodelete testdir
ls: cannot access dirtodelete: No such file or directory
testdir:
Vous pouvez utiliser:
org.Apache.commons.io.FileUtils.deleteQuietly(destFile);
Supprime un fichier sans générer d'exception. Si file est un répertoire, supprimez-le ainsi que tous les sous-répertoires. La différence entre File.delete () et cette méthode est la suivante: Un répertoire à supprimer ne doit pas nécessairement être vide. Aucune exception n'est levée lorsqu'un fichier ou un répertoire ne peut pas être supprimé.
Une solution optimale qui gère les exceptions de manière cohérente avec l'approche selon laquelle une exception générée par une méthode devrait toujours décrire ce que cette méthode tentait (et échouait) de faire:
private void deleteRecursive(File f) throws Exception {
try {
if (f.isDirectory()) {
for (File c : f.listFiles()) {
deleteRecursive(c);
}
}
if (!f.delete()) {
throw new Exception("Delete command returned false for file: " + f);
}
}
catch (Exception e) {
throw new Exception("Failed to delete the folder: " + f, e);
}
}
Dans les projets hérités, je dois créer du code Java natif. Je crée ce code similaire au code Paulitex. Regarde ça:
public class FileHelper {
public static boolean delete(File fileOrFolder) {
boolean result = true;
if(fileOrFolder.isDirectory()) {
for (File file : fileOrFolder.listFiles()) {
result = result && delete(file);
}
}
result = result && fileOrFolder.delete();
return result;
}
}
Et le test unitaire:
public class FileHelperTest {
@Before
public void setup() throws IOException {
new File("FOLDER_TO_DELETE/SUBFOLDER").mkdirs();
new File("FOLDER_TO_DELETE/SUBFOLDER_TWO").mkdirs();
new File("FOLDER_TO_DELETE/SUBFOLDER_TWO/TEST_FILE.txt").createNewFile();
}
@Test
public void deleteFolderWithFiles() {
File folderToDelete = new File("FOLDER_TO_DELETE");
Assert.assertTrue(FileHelper.delete(folderToDelete));
Assert.assertFalse(new File("FOLDER_TO_DELETE").exists());
}
}
Voici une méthode principale nue qui accepte un argument de ligne de commande. Vous devrez peut-être ajouter votre propre vérification des erreurs ou l’adapter à votre guise.
import Java.io.File;
import Java.io.FileNotFoundException;
import Java.io.IOException;
public class DeleteFiles {
/**
* @param intitial arguments take in a source to read from and a
* destination to read to
*/
public static void main(String[] args)
throws FileNotFoundException,IOException {
File src = new File(args[0]);
if (!src.exists() ) {
System.out.println("FAILURE!");
}else{
// Gathers files in directory
File[] a = src.listFiles();
for (int i = 0; i < a.length; i++) {
//Sends files to recursive deletion method
fileDelete(a[i]);
}
// Deletes original source folder
src.delete();
System.out.println("Success!");
}
}
/**
* @param srcFile Source file to examine
* @throws FileNotFoundException if File not found
* @throws IOException if File not found
*/
private static void fileDelete(File srcFile)
throws FileNotFoundException, IOException {
// Checks if file is a directory
if (srcFile.isDirectory()) {
//Gathers files in directory
File[] b = srcFile.listFiles();
for (int i = 0; i < b.length; i++) {
//Recursively deletes all files and sub-directories
fileDelete(b[i]);
}
// Deletes original sub-directory file
srcFile.delete();
} else {
srcFile.delete();
}
}
}
J'espère que ça aide!
Le code ci-dessous supprime récursivement tous les contenus d'un dossier donné.
boolean deleteDirectory(File directoryToBeDeleted) {
File[] allContents = directoryToBeDeleted.listFiles();
if (allContents != null) {
for (File file : allContents) {
deleteDirectory(file);
}
}
return directoryToBeDeleted.delete();
}
Peut-être qu'une solution à ce problème pourrait être de réimplémenter la méthode delete de la classe File en utilisant le code de la réponse de erickson:
public class MyFile extends File {
... <- copy constructor
public boolean delete() {
if (f.isDirectory()) {
for (File c : f.listFiles()) {
return new MyFile(c).delete();
}
} else {
return f.delete();
}
}
}
Sans Commons IO et <Java SE 7
public static void deleteRecursive(File path){
path.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
if (pathname.isDirectory()) {
pathname.listFiles(this);
pathname.delete();
} else {
pathname.delete();
}
return false;
}
});
path.delete();
}
j'ai codé cette routine qui a 3 critères de sécurité pour une utilisation plus sûre.
package ch.ethz.idsc.queuey.util;
import Java.io.File;
import Java.io.IOException;
/** recursive file/directory deletion
*
* safety from erroneous use is enhanced by three criteria
* 1) checking the depth of the directory tree T to be deleted
* against a permitted upper bound "max_depth"
* 2) checking the number of files to be deleted #F
* against a permitted upper bound "max_count"
* 3) if deletion of a file or directory fails, the process aborts */
public final class FileDelete {
/** Example: The command
* FileDelete.of(new File("/user/name/myapp/recordings/log20171024"), 2, 1000);
* deletes given directory with sub directories of depth of at most 2,
* and max number of total files less than 1000. No files are deleted
* if directory tree exceeds 2, or total of files exceed 1000.
*
* abort criteria are described at top of class
*
* @param file
* @param max_depth
* @param max_count
* @return
* @throws Exception if criteria are not met */
public static FileDelete of(File file, int max_depth, int max_count) throws IOException {
return new FileDelete(file, max_depth, max_count);
}
// ---
private final File root;
private final int max_depth;
private int removed = 0;
/** @param root file or a directory. If root is a file, the file will be deleted.
* If root is a directory, the directory tree will be deleted.
* @param max_depth of directory visitor
* @param max_count of files to delete
* @throws IOException */
private FileDelete(final File root, final int max_depth, final int max_count) throws IOException {
this.root = root;
this.max_depth = max_depth;
// ---
final int count = visitRecursively(root, 0, false);
if (count <= max_count) // abort criteria 2)
visitRecursively(root, 0, true);
else
throw new IOException("more files to be deleted than allowed (" + max_count + "<=" + count + ") in " + root);
}
private int visitRecursively(final File file, final int depth, final boolean delete) throws IOException {
if (max_depth < depth) // enforce depth limit, abort criteria 1)
throw new IOException("directory tree exceeds permitted depth");
// ---
int count = 0;
if (file.isDirectory()) // if file is a directory, recur
for (File entry : file.listFiles())
count += visitRecursively(entry, depth + 1, delete);
++count; // count file as visited
if (delete) {
final boolean deleted = file.delete();
if (!deleted) // abort criteria 3)
throw new IOException("cannot delete " + file.getAbsolutePath());
++removed;
}
return count;
}
public int deletedCount() {
return removed;
}
public void printNotification() {
int count = deletedCount();
if (0 < count)
System.out.println("deleted " + count + " file(s) in " + root);
}
}
Alors que les fichiers peuvent facilement être supprimés en utilisant file.delete (), les répertoires doivent être vides pour pouvoir être supprimés. Utilisez la récursivité pour le faire facilement. Par exemple:
public static void clearFolders(String[] args) {
for(String st : args){
File folder = new File(st);
if (folder.isDirectory()) {
File[] files = folder.listFiles();
if(files!=null) {
for(File f: files) {
if (f.isDirectory()){
clearFolders(new String[]{f.getAbsolutePath()});
f.delete();
} else {
f.delete();
}
}
}
}
}
}
Eh bien, supposons un exemple,
import Java.io.File;
import Java.io.IOException;
public class DeleteDirectory
{
private static final String folder = "D:/project/Java";
public static void main(String[] args) throws IOException
{
File fl = new File(folder);
if(!fl.exists()) // checking if directory exists
{
System.out.println("Sorry!! directory doesn't exist.");
}
else
{
DeleteDirectory dd = new DeleteDirectory();
dd.deleteDirectory(fl);
}
}
public void deleteDirectory(File file) throws IOException
{
if(file.isDirectory())
{
if(file.list().length == 0)
{
deleteEmptyDirectory(file); // here if directory is empty delete we are deleting
}
else
{
File fe[] = file.listFiles();
for(File deleteFile : fe)
{
deleteDirectory(deleteFile); // recursive call
}
if(file.list().length == 0)
{
deleteEmptyDirectory(file);
}
}
}
else
{
file.delete();
System.out.println("File deleted : " + file.getAbsolutePath());
}
}
private void deleteEmptyDirectory(File fi)
{
fi.delete();
System.out.println("Directory deleted : " + fi.getAbsolutePath());
}
}
Pour plus d'informations, consultez les ressources ci-dessous.