Рекурсивное удаление каталогов в Java

Есть ли способ рекурсивно удалять целые каталоги в Java?

В обычном случае можно удалить пустой каталог. Однако когда дело доходит до удаления целых каталогов с содержимым, это уже не так просто.

Как удалить целые каталоги с содержимым в Java?


person paweloque    schedule 22.04.2009    source источник
comment
File.delete () должен просто возвращать false при вызове с непустым каталогом.   -  person Ben S    schedule 23.04.2009
comment
Если вы используете Java 8, см. Ответ @ RoK.   -  person Robin    schedule 20.09.2017


Ответы (26)


Вам следует проверить Apache commons-io. У него есть FileUtils, который будет делать то, что вы хотите.

FileUtils.deleteDirectory(new File("directory"));
person Steve K    schedule 22.04.2009
comment
Эта функция, вероятно, является оболочкой для кода, который Эриксон предоставил в своем ответе. - person paweloque; 23.04.2009
comment
Это немного более тщательно. Он правильно обрабатывает такие вещи, как символические ссылки в Linux / Unix. svn.apache.org/viewvc/commons/proper/io/trunk/src/java/org/ - person Steve K; 23.04.2009
comment
@Steve K, URL-адрес теперь: svn.apache.org/viewvc/commons/proper/io/trunk/src/main/java/org/ - person Richard EB; 12.04.2016
comment
URL @RichardEB теперь: github.com/apache/commons-io/blob/master/src/main/java/org/ - person swe; 06.12.2016
comment
Зачем добавлять еще одну зависимость, если у Java есть возможность из коробки? См. Ответ RoK на этой странице или stackoverflow.com/questions/35988192/ - person foo; 04.07.2017
comment
Мы можем использовать простой Java-код вместо использования зависимостей. Ознакомьтесь с моим ответом. - person pvrforpranavvr; 10.03.2018

С Java 7 мы наконец можем сделать это с помощью надежного обнаружения символических ссылок. (я не считаю, что Apache commons-io имеет надежное обнаружение символических ссылок в настоящее время, поскольку оно не обрабатывает ссылки в Windows, созданные с помощью mklink.)

Ради истории, вот ответ до Java 7, который следует за символическими ссылками.

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);
}
person erickson    schedule 22.04.2009
comment
Я согласен, то, что вы пишете в качестве ответа, - хорошее решение. Однако я бы хотел, чтобы метод File.delete () обрабатывал этот случай автоматически. - person paweloque; 23.04.2009
comment
File.delete () не имеет такой функции. - person Ben S; 23.04.2009
comment
@Erickson: Разве FileNotFoundException не является плохим исключением при неудачном удалении? Если файла действительно больше нет, он, должно быть, уже был удален, что означает, что семантически удаление не было неудачным - оно не имело никакого отношения. И если это не удалось по какой-либо другой причине, это было не потому, что файл не был найден. - person Lawrence Dol; 23.04.2009
comment
@Software Monkey: я просто выбрал FileNotFoundException, потому что это то, что используется практически для каждой файловой проблемы в разрешениях java.io ... и т. Д. Не очень хорошо, но иерархии IOException серьезно не хватает. Я надеюсь, что библиотека NIO2 здесь намного лучше. - person erickson; 23.04.2009
comment
Будьте ОЧЕНЬ ОСТОРОЖНЫ. Это приведет к разыменованию символических ссылок. Если вы, например, linux, и иметь папку foo со ссылкой foo/link, так что link->/, вызов delete(new File(foo)) удалит столько файловой системы, сколько разрешено вашему пользователю !! - person Miquel; 07.06.2013
comment
@Miquel. В этом нет смысла. Почему нам нужно быть осторожными? Несомненно, суть предоставленного кода заключается в том, чтобы удалить весь каталог, что, по-видимому, и делает. Я не понимаю, в чем здесь опасность. - person Joehot200; 05.02.2016
comment
@ Joehot200, вы правы, вызов delete для символической ссылки каталога не удалит каталог, а только саму символическую ссылку. Для удаления каталога фактически потребуется явное следование символической ссылке с использованием ReadSymbolicLink. Виноват! Хорошо подмечено - person Miquel; 07.02.2016
comment
Следование символическим ссылкам во время рекурсивного удаления невероятно опасно. Я потерял незаменимую коллекцию фото и видео, потому что первая версия gnome-vfs случайно установила следующие символические ссылки в качестве поведения по умолчанию во время рекурсивного удаления. Это не стандартное поведение для rm по уважительной причине! - person Luke Hutchison; 11.04.2017
comment
Как это работает по сравнению с rm -rf? - person Albert Hendriks; 25.02.2020

В Java 7+ вы можете использовать класс Files. Код очень простой:

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;
   }
});
person Tomasz Dzięcielewski    schedule 13.01.2015
comment
Это решение кажется очень элегантным и вообще не содержит логики обхода каталогов! - person Zero3; 28.11.2015
comment
Найти жемчужное погружение глубоко в океан ... Это, безусловно, лучшее решение, которое я нашел. Пришлось глубоко нырнуть, чтобы найти его. Блестяще! - person Basil Musa; 27.12.2015
comment
Код НЕ очень прост, чтобы просто удалить каталог :-) Но я считаю, что это лучшее решение для чистой java. - person Mat; 11.01.2017
comment
Обратите внимание, что использованная здесь перегрузка walkFileTree не следует по символическим ссылкам. (Документ Javadoc: docs.oracle.com/javase/7/docs/api/java/nio/file/) - person Stephan; 16.09.2017
comment
Вам, вероятно, следует вызвать super.postVisitDirectory(dir, exc); в вашем postVisitDirectory методе, чтобы взорвать, если обход не может перечислить каталог. - person Tom Anderson; 21.03.2019

Однострочное решение (Java8) для рекурсивного удаления всех файлов и каталогов, включая начальный каталог:

Files.walk(Paths.get("c:/dir_to_delete/"))
                .map(Path::toFile)
                .sorted((o1, o2) -> -o1.compareTo(o2))
                .forEach(File::delete);

Мы используем компаратор для обратного порядка, иначе File :: delete не сможет удалить, возможно, непустой каталог. Итак, если вы хотите сохранить каталоги и удалять только файлы, просто удалите компаратор в sorted () или полностью удалите сортировку и добавьте фильтр файлов:

Files.walk(Paths.get("c:/dir_to_delete/"))
                .filter(Files::isRegularFile)
                .map(Path::toFile)
                .forEach(File::delete);
person RoK    schedule 16.02.2017
comment
Вам нужно изменить сортировку в первом на .sorted (Comparator :: reverseOrder), чтобы удалить все каталоги. В противном случае родительский каталог будет упорядочен до дочернего и, следовательно, не будет удален, поскольку он не пуст. Отличный ответ для тех, кто использует Java 8! - person Robin; 20.09.2017
comment
Правильный способ - .sorted(Comparator.reverseOrder()) Предложение Comparator::reverseOrder не. См .: stackoverflow.com/questions/43036611/ - person user1156544; 09.05.2018
comment
Робин, обратите внимание на минус в -o1.compareTo (o2). Это то же самое, что и .sorted (Comparator.reverseOrder) - person RoK; 05.12.2018
comment
Files.walk является последовательным? Или в этом ответе нужно forEachOrdered вместо forEach, чтобы не пытаться удалить непустые каталоги? - person Silwing; 30.10.2019
comment
Просто используйте: .sorted((f1, f2) -> f2.compareTo(f1)), сравнивая f2 с f1 вместо f1 с f2. - person Beto Neto; 17.02.2020
comment
Есть проблема с этим ответом: поток, возвращаемый walk(), должен быть закрыт, потому что он содержит ссылки на один или несколько открытых каталогов (Javadoc). - person rolve; 18.09.2020
comment
Наверное, это отличный лайфхак. - person Antoniossss; 15.12.2020

В Java 7 добавлена ​​поддержка движущихся каталогов с обработкой символических ссылок:

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

Я использую это как отступление от методов, специфичных для платформы (в этом непроверенном коде):

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 взят из Apache Commons Lang. Процессы являются частными, но их поведение должно быть очевидным.)

person Trevor Robinson    schedule 31.12.2011
comment
Я обнаружил одну проблему с Files.walkFileTree - этого недостаточно для реализации версии рекурсивного удаления, когда вы продолжаете удалять файлы, пока у вас не закончатся варианты. Этого достаточно для отказоустойчивой версии, но отказоустойчивость - это не всегда то, что вам нужно (например, если вы очищаете временные файлы, вы хотите удалить сейчас, а не быстро.) - person Trejkaz; 03.05.2013
comment
Я не понимаю, почему это правда. Вы можете обрабатывать ошибки как хотите - от вас не требуется быстро выходить из строя. Единственная проблема, которую я мог предвидеть, заключается в том, что он может не обрабатывать новые файлы, создаваемые во время обхода текущего каталога, но это уникальная ситуация, лучше подходящая для пользовательского решения. - person Trevor Robinson; 03.05.2013
comment
Если вы подавите ошибку из visitFile и вызовете walkFileTree для одного файла, который завершится сбоем, вы не получите ошибки (поэтому visitFile должен распространять любую возникающую ошибку). Если вы удаляете каталог и не можете удалить один файл, вызывается только обратный вызов postVisitDirectory. то есть он не обращается к другим файлам в каталоге, если вы получаете ошибку при посещении одного файла. Это то, что я имею в виду. Я уверен, что, вероятно, есть способ обойти это, но к тому времени, когда мы дошли до этого момента, мы уже написали больше кода, чем традиционная процедура рекурсивного удаления, поэтому мы решили не использовать ее. - person Trejkaz; 06.05.2013
comment
Спасибо за ваш 1-й код, он был полезен для меня, но мне пришлось его изменить, потому что он не завершил простое дельтерево: мне пришлось проигнорировать исключение в postVisitDirectory и вернуть CONTINUE, несмотря на то, что следующее простое дерево не могло быть полностью удалено : Каталог, внутри которого был другой каталог, внутри которого находился один файл. Все это настолько просто / нормально, насколько возможно, в Windows. - person Dreamspace President; 18.11.2015
comment
Все началось с полученного мной исключения java.nio.file.DirectoryNotEmptyException. Выяснил случай, когда используется visitFileFailed. Если в вашей структуре каталогов есть ссылка типа соединения в windows. Это может вызвать у вас 2 проблемы: *) Files.walkFileTree переходит по ссылке на перекресток и удаляет все там. *) Если целевой каталог соединения уже удален, то при синтаксическом анализе ссылки с помощью Files.walkFileTree возникает ошибка NoSuchFileException, которая перехватывается в visitFileFailed. - person Andres Luuk; 02.11.2018
comment
Выполнение большинства операций ввода-вывода по пути нежелательной почты не удастся. И все следующие логические значения являются ложными: Files.exists (файл), Files.isDirectory (файл), Files.isSymbolicLink (файл). Но вы можете получить некоторую информацию из пути с помощью: BasicFileAttributes attrs = Files.readAttributes (file, BasicFileAttributes .class, LinkOption.NOFOLLOW_LINKS); логическое isJunction = attrs.isDirectory () && attrs.isOther (); - person Andres Luuk; 02.11.2018

Только что увидел, что мое решение более или менее похоже на решение Эриксона, только упаковано как статический метод. Бросьте это куда-нибудь, это намного легче, чем установка всех Apache Commons для чего-то, что (как вы можете видеть) довольно просто.

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();
    }
}
person Paulitex    schedule 26.10.2010

Решение со стеком и без рекурсивных методов:

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();
    }
}
person trianam    schedule 26.04.2012
comment
+1 за использование стека. Это будет работать с каталогами, которые содержат глубокие уровни вложенных подкаталогов, в то время как другие подходы на основе стека не будут работать. - person Nathan Osman; 10.03.2013
comment
Видя, что у вас обычно нет проблем с вложением пары сотен вызовов методов, я думаю, что вы, вероятно, столкнетесь с ограничениями файловой системы намного раньше. - person Bombe; 14.09.2013
comment
Будьте осторожны с list* методами для класса java.io.File. Из документации Javadocs: возвращает null, если это абстрактное имя пути не обозначает каталог или если возникает ошибка ввода-вывода. Итак: if (currList.length > 0) { становится if (null != currList && currList.length > 0) { - person kevinarpe; 11.11.2013
comment
Я использую ArrayDeque вместо Stack, который немного быстрее. (несинхронизировано) - person Wytze; 30.10.2015

Guava имел _ 1_ поддерживается до Guava 9.

Из Гуава 10:

Устарело. Этот метод плохо обнаруживает символические ссылки и условия гонки. Эта функциональность может поддерживаться надлежащим образом только с помощью командного интерпретатора операционной системы, такой как rm -rf или del /s. Этот метод планируется удалить из Guava в выпуске Guava 11.0.

Следовательно, такого метода нет в Guava 11.

person Andrew McKinlay    schedule 10.06.2010
comment
Очень жаль. Обстрел кажется немного грубым и непростым. Если версия Apache Commons работает правильно, то, по-видимому, ее можно реализовать. - person Andrew McKinlay; 31.12.2011
comment
@andrew Реализация Apache Commons должна иметь проблемы, аналогичные тем, которые заставляют Guava удалить их реализацию, см. code.google.com/p/guava-libraries/issues/detail?id=365 - person orip; 20.05.2012
comment
Версия apache commons обнаруживает символические ссылки и просто не просматривает дочерние элементы файла. - person Ajax; 20.08.2016
comment
Guava 21.0 добавил это как MoreFiles.deleteRecursively (). - person Robert Fleming; 05.01.2018

Если у вас есть Spring, вы можете использовать FileSystemUtils.deleteRecursively:

import org.springframework.util.FileSystemUtils;

boolean success = FileSystemUtils.deleteRecursively(new File("directory"));
person Ben Hutchison    schedule 14.01.2015

for(Path p : Files.walk(directoryToDelete).
        sorted((a, b) -> b.compareTo(a)). // reverse; files before dirs
        toArray(Path[]::new))
{
    Files.delete(p);
}

Или, если вы хотите обработать IOException:

Files.walk(directoryToDelete).
    sorted((a, b) -> b.compareTo(a)). // reverse; files before dirs
    forEach(p -> {
        try { Files.delete(p); }
        catch(IOException e) { /* ... */ }
      });
person user3669782    schedule 28.10.2014
comment
Это помогло мне придумать версию Scala: Files.walk(path).iterator().toSeq.reverse.foreach(Files.delete) - person James Ward; 19.02.2016
comment
Действительно ли необходима сортировка? Метод walk уже гарантирует обход в глубину. - person VGR; 01.09.2016
comment
Компаратор может быть переработан из Collections.reverseOrder(), поэтому ваш код будет for (Path p : Files.walk(directoryToDelete).sorted(reverseOrder()).toArray(Path[]::new)), если он был импортирован статически. - person namero999; 06.10.2016
comment
@ namero999 Вы имеете в виду Comparator.reverseOrder? Files.walk(dir) .sorted(Comparator.reverseOrder()) .toArray(Path[]::new)) - person Jeff; 23.02.2017
comment
@Jeff совершенно уверен, что вы правы, в основном по памяти :) - person namero999; 24.02.2017

Два способа потерпеть неудачу с символическими ссылками и приведенным выше кодом ... и не знаю решения.

Путь # 1

Запустите это, чтобы создать тест:

echo test > testfile
mkdir dirtodelete
ln -s badlink dirtodelete/badlinktodelete

Здесь вы видите свой тестовый файл и тестовый каталог:

$ ls testfile dirtodelete
testfile

dirtodelete:
linktodelete

Затем запустите свой commons-io deleteDirectory (). Вылетает, говоря, что файл не найден. Не уверен, что здесь делают другие примеры. Команда Linux rm просто удалит ссылку, а также rm -r в каталоге.

Exception in thread "main" java.io.FileNotFoundException: File does not exist: /tmp/dirtodelete/linktodelete

Способ # 2

Запустите это, чтобы создать тест:

mkdir testdir
echo test > testdir/testfile
mkdir dirtodelete
ln -s ../testdir dirtodelete/dirlinktodelete

Здесь вы видите свой тестовый файл и тестовый каталог:

$ ls dirtodelete testdir
dirtodelete:
dirlinktodelete

testdir:
testfile

Затем запустите свой commons-io deleteDirectory () или пример кода, опубликованный людьми. Он удаляет не только каталог, но и ваш тестовый файл, который находится за пределами удаляемого каталога. (Он неявно разыменовывает каталог и удаляет его содержимое). rm -r удалит только ссылку. Вам нужно использовать что-то вроде этого для удаления разыменованных файлов: "find -L dirtodelete -type f -exec rm {} \;".

$ ls dirtodelete testdir
ls: cannot access dirtodelete: No such file or directory
testdir:
person Peter    schedule 13.05.2011

Вы можете использовать:

org.apache.commons.io.FileUtils.deleteQuietly(destFile);

Удаляет файл, не вызывая исключения. Если файл является каталогом, удалите его и все подкаталоги. Разница между File.delete () и этим методом заключается в следующем: удаляемый каталог не должен быть пустым. Если файл или каталог не могут быть удалены, исключения не создаются.

person Jan-Terje Sørensen    schedule 11.02.2013

Оптимальное решение, которое обрабатывает исключение в соответствии с подходом, согласно которому исключение, созданное методом, всегда должно описывать, что этот метод пытался (и не смог) сделать:

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);
    }
}
person AgilePro    schedule 25.12.2017

В старых проектах мне нужно создавать собственный код Java. Я создаю этот код, аналогичный коду Paulitex. Видеть, что:

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

И модульный тест:

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

}
person Wendel    schedule 16.05.2016

Приведенный ниже код рекурсивно удаляет все содержимое в данной папке.

boolean deleteDirectory(File directoryToBeDeleted) {
    File[] allContents = directoryToBeDeleted.listFiles();
    if (allContents != null) {
        for (File file : allContents) {
            deleteDirectory(file);
        }
    }
    return directoryToBeDeleted.delete();
}
person pvrforpranavvr    schedule 10.03.2018

Вот простой основной метод, который принимает аргумент командной строки, вам может потребоваться добавить свою собственную проверку ошибок или сформировать ее так, как вы считаете нужным.

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

Надеюсь, это поможет!

person glue    schedule 23.04.2009

Guava предоставляет однострочник: _ 1_ .

В отличие от многих распространенных примеров, он учитывает символические ссылки и не будет (по умолчанию) удалять файлы за пределами указанного пути.

person dimo414    schedule 30.05.2020

Возможно, решением этой проблемы может быть повторная реализация метода удаления класса File с использованием кода из ответа Эриксона:

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();
    }
  }
}
person paweloque    schedule 22.04.2009
comment
Я думаю, что это реализовано так, чтобы имитировать поведение большинства утилит командной оболочки, таких как rm, rmdir и del. Из двух альтернатив текущая реализация определенно минимизирует общий потенциал удивления (и гнева). Это не изменится. - person erickson; 23.04.2009
comment
Как правило, я вижу расширенные только пакеты Java JRE от Swing. Обычно расширение других классов, таких как java.io.File, - плохая идея, поскольку это может привести к неожиданным действиям. - person Eddie; 23.04.2009

Без Commons IO и ‹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();
        }
person Alexander Sidikov Pfeif    schedule 18.09.2015

// Java 8 с лямбдой и потоком, если параметр - это каталог

static boolean delRecursive(File dir) {
    return Arrays.stream(dir.listFiles()).allMatch((f) -> f.isDirectory() ? delRecursive(f) : f.delete()) && dir.delete();
}

// если параметр - это файл или каталог

static boolean delRecursive(File fileOrDir) {
    return fileOrDir.isDirectory() ? Arrays.stream(fileOrDir.listFiles()).allMatch((f) -> delRecursive(f)) && fileOrDir.delete() : fileOrDir.delete();
}
person Yessy    schedule 07.08.2020

Хотя файлы можно легко удалить с помощью file.delete (), для удаления каталоги должны быть пустыми. Используйте рекурсию, чтобы сделать это легко. Например:

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();
                        }
                    }
                }
            }
        }
    }
person Bharat Singh    schedule 20.02.2015

Я закодировал эту процедуру, которая имеет 3 критерия безопасности для более безопасного использования.

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);
    }
}
person datahaki    schedule 24.10.2017

rm -rf был намного эффективнее, чем FileUtils.deleteDirectory.

После обширного тестирования мы обнаружили, что использование rm -rf было в несколько раз быстрее, чем использование FileUtils.deleteDirectory.

Конечно, если у вас небольшой или простой каталог, это не имеет значения, но в нашем случае у нас было несколько гигабайт и глубоко вложенные подкаталоги, где это заняло бы более 10 минут с FileUtils.deleteDirectory и только 1 минуту с rm -rf.

Вот примерная реализация этого на Java:

// Delete directory given and all subdirectories and files (i.e. recursively).
//
static public boolean deleteDirectory( File file ) throws IOException, InterruptedException {

    if ( file.exists() ) {

        String deleteCommand = "rm -rf " + file.getAbsolutePath();
        Runtime runtime = Runtime.getRuntime();

        Process process = runtime.exec( deleteCommand );
        process.waitFor();

        return true;
    }

    return false;

}

Стоит попробовать, если вы имеете дело с большими или сложными каталогами.

person Joshua Pinter    schedule 16.10.2019
comment
@ cricket_007 Какие платформы? - person Joshua Pinter; 05.05.2020
comment
Windows? OpenWrt? BSD? - person OneCricketeer; 05.05.2020
comment
@ cricket_007 Определенно не Windows. Это было протестировано и использовалось на Android и macOS. - person Joshua Pinter; 05.05.2020

Что ж, давайте возьмем пример,

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

Для получения дополнительной информации см. Ресурсы ниже

Удалить каталог

person Shiva    schedule 20.12.2017

person    schedule
comment
Расширенная версия с логическим возвращаемым значением и без дублирования: pastebin.com/PqJyzQUx - person Erik Kaplun; 06.03.2014

person    schedule
comment
Хороший код, но есть одна ошибка, при исправлении работает. Строка f.delete() под deleteDirectory(f) вызовет исключение NoSuchFileException, поскольку deleteDirectory(f) уже удаляет этот файл. Каждый каталог станет путем при передаче в deleteDirectory(f) и удалении path.delete(). Следовательно, нам не нужно f.delete() в разделе if f.isDerectory. Итак, просто удалите f.delete(); из каталога deleteDirectory (f), и он заработает. - person Trieu Nguyen; 18.05.2020