Java.nio:最简洁的递归目录删除
Java.nio: most concise recursive directory delete
我目前正在尝试递归删除目录...奇怪的是,我能找到的最短代码是以下结构,采用了 ad-hoc 内部 class 并且在 访问者模式中 ...
Path rootPath = Paths.get("data/to-delete");
try {
Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println("delete file: " + file.toString());
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
System.out.println("delete dir: " + dir.toString());
return FileVisitResult.CONTINUE;
}
});
} catch(IOException e){
e.printStackTrace();
}
来源:here
鉴于新的 nio
API 消除了如此多的混乱和样板,这让人感觉非常笨拙和冗长...
是否有任何更短的方法来实现强制的递归目录删除?
我正在寻找纯原生 Java 1.8 方法,所以请不要 link 到外部库...
您可以结合使用 NIO 2 和 Stream API。
Path rootPath = Paths.get("/data/to-delete");
// before you copy and paste the snippet
// - read the post till the end
// - read the javadoc to understand what the code will do
//
// a) to follow softlinks (removes the linked file too) use
// Files.walk(rootPath, FileVisitOption.FOLLOW_LINKS)
//
// b) to not follow softlinks (removes only the softlink) use
// the snippet below
try (Stream<Path> walk = Files.walk(rootPath)) {
walk.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.peek(System.out::println)
.forEach(File::delete);
}
Files.walk
- return rootPath
以下的所有 files/directories 包括
.sorted
- 以相反的顺序对列表进行排序,因此目录本身位于包含子目录和文件之后
.map
- 将 Path
映射到 File
.peek
- 是否仅显示处理了哪个条目
.forEach
- 在每个 File
对象上调用 .delete()
方法
编辑 正如@Seby and now cited by @John Dough首先提到的,Files.walk()
应该用在try-with-resource
结构中。感谢两位。
来自 Files.walk javadoc
If timely disposal of file system resources is required, the try-with-resources construct should be used to ensure that the stream's close method is invoked after the stream operations are completed.
编辑
这是一些数字。
目录 /data/to-delete
包含解压缩的 rt.jar
of jdk1.8.0_73 和最近构建的 activemq.
files: 36,427
dirs : 4,143
size : 514 MB
时间(以毫秒为单位)
int. SSD ext. USB3
NIO + Stream API 1,126 11,943
FileVisitor 1,362 13,561
两个版本都在没有打印文件名的情况下执行。最大的限制因素是驱动器。不是实现。
编辑
有关选项 FileVisitOption.FOLLOW_LINKS
的一些附加信息。
假设文件和目录结构如下
/data/dont-delete/bar
/data/to-delete/foo
/data/to-delete/dont-delete -> ../dont-delete
正在使用
Files.walk(rootPath, FileVisitOption.FOLLOW_LINKS)
将跟随符号链接,文件 /tmp/dont_delete/bar
也将被删除。
正在使用
Files.walk(rootPath)
不会跟随符号链接,文件 /tmp/dont_delete/bar
不会被删除。
注意:切勿在不了解其作用的情况下将代码用作复制和粘贴。
以下解决方案不需要从 Path 到 File 对象的转换:
Path rootPath = Paths.get("/data/to-delete");
final List<Path> pathsToDelete = Files.walk(rootPath).sorted(Comparator.reverseOrder()).collect(Collectors.toList());
for(Path path : pathsToDelete) {
Files.deleteIfExists(path);
}
如果你必须只使用 Java 7 和 NIO
Path path = Paths.get("./target/logs");
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 postVisitDirectory(Path dir, IOException exc)
throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
如果您已经将 Spring 核心作为项目的一部分,这里有一个简单的方法:
FileSystemUtils.deleteRecursively(dir);
Files.walk(pathToBeDeleted).sorted(Comparator.reverseOrder()).forEach(Files::delete);
如果 "timely disposal of file system resources is required",您将需要 "try with resources" 模式来关闭流。
此外,这可能是一个不受欢迎的评论,但使用库会更清晰、更易读。共享函数中的代码,不会占用太多space。每个查看您的代码的人都必须验证此代码是否进行了正确的删除,这绝不是显而易见的。
FileUtils.deleteDirectory
from Apache Commons IO 递归删除目录。
示例:
Path pathToBeDeleted = TEMP_DIRECTORY.resolve(DIRECTORY_NAME);
boolean result = FileUtils.deleteDirectory(pathToBeDeleted.toFile());
有关详细信息,请参阅 Delete a Directory Recursively in Java。
我目前正在尝试递归删除目录...奇怪的是,我能找到的最短代码是以下结构,采用了 ad-hoc 内部 class 并且在 访问者模式中 ...
Path rootPath = Paths.get("data/to-delete");
try {
Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println("delete file: " + file.toString());
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
System.out.println("delete dir: " + dir.toString());
return FileVisitResult.CONTINUE;
}
});
} catch(IOException e){
e.printStackTrace();
}
来源:here
鉴于新的 nio
API 消除了如此多的混乱和样板,这让人感觉非常笨拙和冗长...
是否有任何更短的方法来实现强制的递归目录删除?
我正在寻找纯原生 Java 1.8 方法,所以请不要 link 到外部库...
您可以结合使用 NIO 2 和 Stream API。
Path rootPath = Paths.get("/data/to-delete");
// before you copy and paste the snippet
// - read the post till the end
// - read the javadoc to understand what the code will do
//
// a) to follow softlinks (removes the linked file too) use
// Files.walk(rootPath, FileVisitOption.FOLLOW_LINKS)
//
// b) to not follow softlinks (removes only the softlink) use
// the snippet below
try (Stream<Path> walk = Files.walk(rootPath)) {
walk.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.peek(System.out::println)
.forEach(File::delete);
}
Files.walk
- returnrootPath
以下的所有 files/directories 包括.sorted
- 以相反的顺序对列表进行排序,因此目录本身位于包含子目录和文件之后.map
- 将Path
映射到File
.peek
- 是否仅显示处理了哪个条目.forEach
- 在每个File
对象上调用.delete()
方法
编辑 正如@Seby and now cited by @John Dough首先提到的,Files.walk()
应该用在try-with-resource
结构中。感谢两位。
来自 Files.walk javadoc
If timely disposal of file system resources is required, the try-with-resources construct should be used to ensure that the stream's close method is invoked after the stream operations are completed.
编辑
这是一些数字。
目录 /data/to-delete
包含解压缩的 rt.jar
of jdk1.8.0_73 和最近构建的 activemq.
files: 36,427
dirs : 4,143
size : 514 MB
时间(以毫秒为单位)
int. SSD ext. USB3
NIO + Stream API 1,126 11,943
FileVisitor 1,362 13,561
两个版本都在没有打印文件名的情况下执行。最大的限制因素是驱动器。不是实现。
编辑
有关选项 FileVisitOption.FOLLOW_LINKS
的一些附加信息。
假设文件和目录结构如下
/data/dont-delete/bar
/data/to-delete/foo
/data/to-delete/dont-delete -> ../dont-delete
正在使用
Files.walk(rootPath, FileVisitOption.FOLLOW_LINKS)
将跟随符号链接,文件 /tmp/dont_delete/bar
也将被删除。
正在使用
Files.walk(rootPath)
不会跟随符号链接,文件 /tmp/dont_delete/bar
不会被删除。
注意:切勿在不了解其作用的情况下将代码用作复制和粘贴。
以下解决方案不需要从 Path 到 File 对象的转换:
Path rootPath = Paths.get("/data/to-delete");
final List<Path> pathsToDelete = Files.walk(rootPath).sorted(Comparator.reverseOrder()).collect(Collectors.toList());
for(Path path : pathsToDelete) {
Files.deleteIfExists(path);
}
如果你必须只使用 Java 7 和 NIO
Path path = Paths.get("./target/logs");
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 postVisitDirectory(Path dir, IOException exc)
throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
如果您已经将 Spring 核心作为项目的一部分,这里有一个简单的方法:
FileSystemUtils.deleteRecursively(dir);
Files.walk(pathToBeDeleted).sorted(Comparator.reverseOrder()).forEach(Files::delete);
如果 "timely disposal of file system resources is required",您将需要 "try with resources" 模式来关闭流。
此外,这可能是一个不受欢迎的评论,但使用库会更清晰、更易读。共享函数中的代码,不会占用太多space。每个查看您的代码的人都必须验证此代码是否进行了正确的删除,这绝不是显而易见的。
FileUtils.deleteDirectory
from Apache Commons IO 递归删除目录。
示例:
Path pathToBeDeleted = TEMP_DIRECTORY.resolve(DIRECTORY_NAME);
boolean result = FileUtils.deleteDirectory(pathToBeDeleted.toFile());
有关详细信息,请参阅 Delete a Directory Recursively in Java。