在Java中recursion地删除目录
有没有办法在Java中recursion删除整个目录?
在正常情况下,可以删除一个空目录。 但是,当用内容删除整个目录时,就不那么简单了。
你如何删除Java中的内容的整个目录?
你应该看看Apache的commons-io 。 它有一个FileUtils类,将做你想做的。
FileUtils.deleteDirectory(new File("directory"));
使用Java 7,我们终于可以通过可靠的符号链接检测来做到这一点。 (我不认为Apache的commons-io此时有可靠的符号链接检测,因为它不处理用mklink
创build的Windows上的mklink
。)
为了历史的缘故,这是一个Java之前的答案,它遵循符号链接。
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); }
在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; } });
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; } } }); }
我将其用作平台特定方法的回退(在这个未经testing的代码中):
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 。进程是私有的,但其行为应该是显而易见的。)
刚刚看到我的解决scheme与erickson差不多,只是打包成一个静态方法。 把它放在某个地方,比安装所有的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 && FileUtils.deleteRecursive(f); } } return ret && path.delete(); } }
单线程解决scheme(Java8)以recursion方式删除所有文件和目录,包括起始目录:
Files.walk(Paths.get("c:/dir_to_delete/")) .map(Path::toFile) .sorted((o1, o2) -> -o1.compareTo(o2)) .forEach(File::delete);
我们使用反向比较器,否则File :: delete将无法删除可能的非空目录。 所以,如果你想保留目录,只删除文件,只需在sorted()中删除比较器或完全删除sorting,并添加文件filter:
Files.walk(Paths.get("c:/dir_to_delete/")) .filter(Files::isRegularFile) .map(Path::toFile) .forEach(File::delete);
一个堆栈和没有recursion方法的解决scheme:
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(); } }
番石榴有Files.deleteRecursively(File)
支持,直到番石榴9 。
从番石榴10 :
已过时。 这种方法遇到糟糕的符号链接检测和竞争条件。 这个function只有通过脱壳到一个操作系统命令(如
rm -rf
或del /s
才能被支持。 这种方法计划从Guava版本11.0中的Guava中移除。
因此,在番石榴11中没有这样的方法。
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) { /* ... */ } });
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(); }
如果你有Spring,你可以使用FileSystemUtils.deleteRecursively :
import org.springframework.util.FileSystemUtils; boolean success = FileSystemUtils.deleteRecursively(new File("directory"));
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(); } }
两种方法失败,符号链接和上面的代码…并不知道解决scheme。
方式#1
运行这个来创build一个testing:
echo test > testfile mkdir dirtodelete ln -s badlink dirtodelete/badlinktodelete
在这里你可以看到你的testing文件和testing目录:
$ ls testfile dirtodelete testfile dirtodelete: linktodelete
然后运行你的commons-io deleteDirectory()。 它崩溃说没有find该文件。 不知道其他例子在这里做什么。 Linux rm命令将简单地删除该链接,并且该目录上的rm -r也会被删除。
Exception in thread "main" java.io.FileNotFoundException: File does not exist: /tmp/dirtodelete/linktodelete
方式2
运行这个来创build一个testing:
mkdir testdir echo test > testdir/testfile mkdir dirtodelete ln -s ../testdir dirtodelete/dirlinktodelete
在这里你可以看到你的testing文件和testing目录:
$ ls dirtodelete testdir dirtodelete: dirlinktodelete testdir: testfile
然后运行你的commons-io deleteDirectory()或者发布的示例代码。 它不但删除了目录,而且还删除了被删除目录之外的testfile。 (它隐式提取目录,并删除内容)。 rm -r只会删除链接。 您需要使用像这样删除解除引用的文件:“find -L dirtodelete -type f -exec rm {} \;”。
$ ls dirtodelete testdir ls: cannot access dirtodelete: No such file or directory testdir:
你可以使用:
org.apache.commons.io.FileUtils.deleteQuietly(destFile);
删除一个文件,永远不会抛出一个exception。 如果文件是一个目录,删除它和所有的子目录。 File.delete()和此方法之间的区别是:要删除的目录不一定是空的。 无法删除文件或目录时,不会引发exception。
在遗留项目中,我需要创build本地Java代码。 我创build了类似于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; } }
而unit testing:
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()); } }
这里是接受命令行参数的一个简单的主要方法,你可能需要附加你自己的错误检查或模拟它,以适应你的看法。
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(); } } }
我希望有帮助!
也许这个问题的解决scheme可能是重新实现File类的删除方法,使用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(); } } }
尽pipe可以使用file.delete()轻松删除文件,但是需要将目录清空才能删除。 使用recursion来做到这一点很容易。 例如:
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(); } } } } } }
没有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(); }
我编写了这个程序,有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); } }