Удалить каталоги рекурсивно в Java - PullRequest
357 голосов
/ 23 апреля 2009

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

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

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

Ответы [ 23 ]

438 голосов
/ 23 апреля 2009

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

FileUtils.deleteDirectory(new File("directory"));
188 голосов
/ 23 апреля 2009

С 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);
}
139 голосов
/ 13 января 2015

В 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;
   }
});
65 голосов
/ 31 декабря 2011

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

53 голосов
/ 16 февраля 2017

Однострочное решение (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);
30 голосов
/ 26 октября 2010

Только что увидел, что мое решение более или менее совпадает с решением Эриксона, просто упаковано как статический метод. Оставьте это где-нибудь, это намного легче, чем устанавливать все 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();
    }
}
18 голосов
/ 26 апреля 2012

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

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();
    }
}
14 голосов
/ 10 июня 2010

Гуава имел Files.deleteRecursively(File) поддерживается до Гуава 9 .

С Гуава 10 :

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

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

такого метода нет.
13 голосов
/ 14 января 2015

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

import org.springframework.util.FileSystemUtils;

boolean success = FileSystemUtils.deleteRecursively(new File("directory"));
11 голосов
/ 28 октября 2014
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) { /* ... */ }
      });
...