在Java中是否有一种方法可以递归地删除整个目录?

在正常情况下,可以删除空目录。 然而,当涉及到删除包含内容的整个目录时,就不再那么简单了。

如何在Java中删除包含内容的整个目录?


当前回答

番石榴21.0及以上版本

有一个void deleterecursive (Path Path, RecursiveDeleteOption…)options)会抛出从Guava 21.0开始可用的MoreFiles类的IOException静态方法。

请参阅Javadoc文档:

public static void deleteRecursively(Path path, RecursiveDeleteOption... options) throws IOException Deletes the file or directory at the given path recursively. Deletes symbolic links, not their targets (subject to the caveat below). If an I/O exception occurs attempting to read, open or delete any file under the given directory, this method skips that file and continues. All such exceptions are collected and, after attempting to delete all files, an IOException is thrown containing those exceptions as suppressed exceptions.

其他回答

刚刚看到我的解决方案或多或少与埃里克森的相同,只是包装成一个静态方法。把它放到某个地方,它的重量比安装所有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();
    }
}

在Guava 9之前,Guava一直支持files . deleterecursive (File)。

来自番石榴10:

弃用。该方法存在符号链接检测差和竞态条件差的问题。只有使用操作系统命令(如rm -rf或del /s)才能适当地支持此功能。这个方法计划在Guava 11.0版本中从Guava中移除。

因此,在番石榴11中没有这种方法。

Guava提供了一行代码:morefiles . deleterecursive()。

与许多共享的示例不同,它只考虑符号链接,并且不会(默认情况下)删除所提供路径之外的文件。

为了更安全的使用,我编写了有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);
    }
}

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