Java.nio:最简洁的递归目录删除

Java.nio:最简洁的递归目录删除,java,directory,nio,delete-file,Java,Directory,Nio,Delete File,我正在尝试递归删除一个目录。。。奇怪的是,我能找到的最短的一段代码是以下构造,采用了一个特殊的内部类和访问者模式 Path rootPath=Path.get(“数据/要删除”); 试一试{ walkFileTree(根路径,新的SimpleFileVisitor(){ @凌驾 公共文件VisitResult visitFile(路径文件,基本文件属性属性属性)引发IOException{ System.out.println(“删除文件:+file.toString()); 文件。删除(文件)

我正在尝试递归删除一个目录。。。奇怪的是,我能找到的最短的一段代码是以下构造,采用了一个特殊的内部类和访问者模式

Path rootPath=Path.get(“数据/要删除”);
试一试{
walkFileTree(根路径,新的SimpleFileVisitor(){
@凌驾
公共文件VisitResult visitFile(路径文件,基本文件属性属性属性)引发IOException{
System.out.println(“删除文件:+file.toString());
文件。删除(文件);
返回FileVisitResult.CONTINUE;
}
@凌驾
公共文件VisitResult postVisitDirectory(路径目录,IOException exc)引发IOException{
删除(dir);
System.out.println(“删除目录:+dir.toString());
返回FileVisitResult.CONTINUE;
}
});
}捕获(IOE异常){
e、 printStackTrace();
}
资料来源:

考虑到新的
nio
api删除了如此多的杂乱和样板文件,这感觉非常笨拙和冗长

有没有较短的方法实现强制递归目录删除?


我正在寻找纯本机Java 1.8方法,因此请不要链接到外部库…

您可以将NIO 2和流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);
}
以毫秒为单位的时间

                    int. SSD     ext. USB3
NIO + Stream API    1,126        11,943
FileVisitor         1,362        13,561
两个版本都是在不打印文件名的情况下执行的。最大的限制因素是驱动力。而不是执行

编辑

有关选项
文件访问的一些附加信息。请点击链接

假设以下文件和目录结构

/data/dont-delete/bar
/data/to-delete/foo
/data/to-delete/dont-delete -> ../dont-delete
使用

将遵循符号链接,文件
/tmp/dont_delete/bar
也将被删除

使用

不会跟随符号链接,并且不会删除文件
/tmp/dont_delete/bar


注意:在不了解代码的功能的情况下,切勿将代码用作复制和粘贴。

以下解决方案不需要从路径到文件对象的转换:

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);
}
Path rootPath=Path.get(“/data/to delete”);
最终列表路径stodelete=Files.walk(rootPath).sorted(Comparator.reverseOrder()).collect(Collectors.toList());
用于(路径:路径删除){
Files.deleteIfExists(路径);
}

如果必须在NIO中仅使用Java 7

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;
  }
});
Path Path=Path.get(“./target/logs”);
walkFileTree(路径,新的SimpleFileVisitor(){
@凌驾
公共文件VisitResult visitFile(路径文件,基本文件属性属性属性)引发IOException{
文件。删除(文件);
返回FileVisitResult.CONTINUE;
}
@凌驾
公共文件VisitResult postVisitDirectory(路径目录,IOException exc)
抛出IOException{
删除(dir);
返回FileVisitResult.CONTINUE;
}
});

如果您已经将Spring Core作为项目的一部分,下面是一个简单的方法:

FileSystemUtils.deleteRecursively(dir);
资料来源:

如果“需要及时处理文件系统资源”,则需要“尝试使用资源”模式来关闭流


此外,这可能是一个不受欢迎的评论,但使用库会更干净、更具可读性。由于代码位于共享函数中,因此不会占用太多空间。查看您的代码的每个人都必须验证此代码是否正确删除,并且其删除并不明显。

FileUtils.deleteDirectory
从递归删除目录

例如:

Path pathToBeDeleted = TEMP_DIRECTORY.resolve(DIRECTORY_NAME);

boolean result = FileUtils.deleteDirectory(pathToBeDeleted.toFile());

有关更多信息,请参见。

这感觉非常笨拙和冗长,为什么?这是一个很好的方法。Java 8
文件。walk
不会给您这样做的机会。因为这会迫使用户重新定义一个简单的递归删除。。。因为这需要15行代码。。。像
Files.deleteRecursively(Path)
这样的东西怎么样,或者一些可选的标志呢?答案是它根本不存在于内置的NIO.2中。您可以使用递归方法处理
文件。list
,但它是相同的,我更喜欢您的解决方案。@fgysin Kotlin的stdlib中有这个。真的没有理由不包括它。@KeksArmee只是Kotlin函数将始终遵循符号链接。@Tunaki看一下我的更新答案。我添加了一些数字。你他妈的为什么要添加
FileVisitOption.FOLLOW\u LINKS
标志?这意味着您将清空符号链接指向的整个目标树。@次优,很抱歉有点粗鲁的评论,但下面的链接只是多余的。它根本不适合topic-starter指定的任务。您不需要
.map(Path::toFile)
.forEach
的参数可以是
文件。改为删除
。@pdxle如果需要处理
文件的
IOException
。在传递给
forEach(…)
的使用者函数中删除(路径p)
。然而
file.delete()
不要抛出一个。这不会关闭流,因此目录本身可能被阻止,并且处于不一致的状态。映射到文件的路径对我来说有一种不好的味道。省略.map()并以.forEach(Files::delete)结尾!注意这里的Files实用程序类的复数形式@Amadán更新了它。不幸的是,@Amadán是不正确的,你不能像这样调用
Files::delete
,因为它会抛出选中的异常,所以你的答案现在不起作用。该死的。不管是谁发明的,希望已经在地狱里燃烧。这可以通过引入捕获异常并将其包装到RuntimeException中的实用程序方法来克服。这是在虚拟文件系统上工作的唯一方法,例如,Zip:
FileSystems.newFileSystem(“jar:file:my.Zip”,…).getPath(“path”)
-只能使用仅NIO的方法删除此路径。您为什么要收集到一个列表并迭代该列表,而不是仅仅用forEach终止流?我想我这样做是因为这样在文件删除过程中更容易捕获异常。我明白了。选中的异常非常糟糕。;-)或者一个米格
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);
}
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;
  }
});
FileSystemUtils.deleteRecursively(dir);
Files.walk(pathToBeDeleted).sorted(Comparator.reverseOrder()).forEach(Files::delete);
Path pathToBeDeleted = TEMP_DIRECTORY.resolve(DIRECTORY_NAME);

boolean result = FileUtils.deleteDirectory(pathToBeDeleted.toFile());