在Java中有没有一种递归删除整个目录的方法?
通常情况下,可以删除空目录。但是当需要删除带有内容的整个目录时,就不那么简单了。
你如何在Java中删除带有内容的整个目录?
在Java中有没有一种递归删除整个目录的方法?
通常情况下,可以删除空目录。但是当需要删除带有内容的整个目录时,就不那么简单了。
你如何在Java中删除带有内容的整个目录?
你应该查看Apache的commons-io。它有一个FileUtils类可以完成你想要的操作。
FileUtils.deleteDirectory(new File("directory"));
使用Java 7,我们终于可以可靠地检测符号链接(reliable symlink detection)。(我认为Apache的commons-io目前没有可靠的符号链接检测,因为它不能处理使用mklink
在Windows上创建的链接。)
出于历史考虑,这里是一个Java 7之前的答案,它遵循符号链接(follows symlinks)。
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);
}
foo
的文件夹和一个链接foo/link
,使得link->/
,调用delete(new File(foo))
将会删除你的文件系统中用户被允许删除的所有内容! - Miquel在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;
}
});
postVisitDirectory
方法中,应该调用super.postVisitDirectory(dir, exc);
来处理如果遍历无法列出目录的情况。请注意不要改变原意,使翻译易于理解。 - Tom Anderson使用一行代码解决(Java8),递归地删除包括起始目录在内的所有文件和目录:
try (var dirStream = Files.walk(Paths.get("c:/dir_to_delete/"))) {
dirStream
.map(Path::toFile)
.sorted(Comparator.reverseOrder())
.forEach(File::delete);
}
我们使用比较器来进行反向排序,否则 File::delete 将无法删除可能非空的目录。因此,如果您想保留目录并仅删除文件,请从 sorted() 中删除比较器 或 完全删除排序并添加文件过滤器:
try (var dirStream = Files.walk(Paths.get("c:/dir_to_delete/"))) {
dirStream
.filter(Files::isRegularFile)
.map(Path::toFile)
.forEach(File::delete);
}
.sorted(Comparator.reverseOrder())
,建议使用 Comparator::reverseOrder
无效。参见:https://dev59.com/9aDia4cB1Zd3GeqPCVoy#43036643 - user1156544walk()
返回的流应该被关闭,因为它“包含对一个或多个打开目录的引用”(Javadoc)。 - rolveJava 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。Processes是私有的,但它的行为应该很明显。)
我看到我的解决方案基本上与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 && deleteRecursive(f);
}
}
return ret && path.delete();
}
}
使用栈而不使用递归方法的解决方案:
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();
}
}
java.io.File
类的list*
方法要小心。根据Javadocs的说明:“如果此抽象路径名不表示目录,或者发生I/O错误,则返回null。”所以:if (currList.length > 0) {
变成了if (null != currList && currList.length > 0) {
。 - kevinarpeimport org.springframework.util.FileSystemUtils;
boolean success = FileSystemUtils.deleteRecursively(new File("directory"));
Guava在Guava 9版本中提供了Files.deleteRecursively(File)
方法。
从Guava 10开始:
已废弃。该方法存在符号链接检测和竞态条件问题。此功能只能通过调用操作系统命令,例如
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) { /* ... */ }
});
Files.walk(path)。iterator()。toSeq.reverse.foreach(Files.delete)
。 - James WardCollections.reverseOrder()
中回收,因此您的代码将是for (Path p : Files.walk(directoryToDelete).sorted(reverseOrder()).toArray(Path[]::new))
假设已经静态导入。 - namero999Comparator.reverseOrder
吗?Files.walk(dir) .sorted(Comparator.reverseOrder()) .toArray(Path[]::new)
- Jeff