Files.createTempDirectory是否会在JVM正常退出后删除目录?

42

Files.createTempDirectory方法会在JVM正常退出后自动删除临时目录吗?还是我需要手动递归删除临时目录内容?

6个回答

64

Files.createTempDirectory()创建的临时目录在系统退出(JVM终止)时不会被删除,除非您配置它们这样做:

可以使用关闭挂钩File.deleteOnExit()机制自动删除目录。

意思是您可以调用:

Path tmp = Files.createTempDirectory(null);
tmp.toFile().deleteOnExit();

然而,只有当目录为空时才能删除它,如File.delete()所示:

删除由此抽象路径名表示的文件或目录。如果此路径名表示目录,则必须先清空该目录才能删除。

因此,如果要删除目录及其内容,我们需要更高级的方法。您可以像这样递归地注册目录及其子目录以进行删除:

public static void recursiveDeleteOnExit(Path path) throws IOException {
  Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
    @Override
    public FileVisitResult visitFile(Path file,
        @SuppressWarnings("unused") BasicFileAttributes attrs) {
      file.toFile().deleteOnExit();
      return FileVisitResult.CONTINUE;
    }
    @Override
    public FileVisitResult preVisitDirectory(Path dir,
        @SuppressWarnings("unused") BasicFileAttributes attrs) {
      dir.toFile().deleteOnExit();
      return FileVisitResult.CONTINUE;
    }
  });
}

注意,这将注册所有当前存在的文件以便删除 - 如果在调用此方法后创建了新文件,则根据File.delete()文档中记录的行为,它们及其父目录将不会被删除。

如果你想要在退出时删除目录,无论该目录的内容如何,你可以使用一个关机挂钩(shutdown-hook),使用几乎相同的方式:

public static void recursiveDeleteOnShutdownHook(final Path path) {
  Runtime.getRuntime().addShutdownHook(new Thread(
    new Runnable() {
      @Override
      public void run() {
        try {
          Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file,
                @SuppressWarnings("unused") BasicFileAttributes attrs)
                throws IOException {
              Files.delete(file);
              return FileVisitResult.CONTINUE;
        }
        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException e)
            throws IOException {
          if (e == null) {
            Files.delete(dir);
            return FileVisitResult.CONTINUE;
          }
          // directory iteration failed
          throw e;
        }
        });
      } catch (IOException e) {
        throw new RuntimeException("Failed to delete "+path, e);
      }
    }}));
}

请注意,反复调用此方法会每次注册一个新的关闭线程,这可能在大规模操作时造成问题。File.deleteOnExit()会存储一组已注册的文件,并在一个关闭钩子中删除所有这些文件。如果您需要以类似的方式删除多个目录,则应实现类似的机制。


10
在Spring中,有一个帮助方法org.springframework.util.FileSystemUtils.deleteRecursively(File root),用于显式递归删除目录。 - Sergey Shcherbakov

28
根据API,不会自动删除目录,您需要使用file.deleteOnExit()方法手动删除目录。
与createTempFile方法一样,此方法仅是临时文件设施的一部分。可以使用关闭挂钩或File.deleteOnExit()机制自动删除目录。

2
@HowardGuo,我认为你传递了null给createTempDirectory方法。 - Pradeep Simha
2
@HowardGuo,http://docs.oracle.com/javase/tutorial/essential/io/dirs.html 这个链接应该会给你更多信息。 - Pradeep Simha
1
我认为文档上说prefix参数可以为空。 - user972946
1
@HowardGuo,是的,前缀可能为空,但其他参数不可以。如果您阅读API文档,该方法接受3个参数,因为您只传递了1个参数,所以您的代码无法编译。 - Pradeep Simha
4
@PradeepSimha - 你为什么说你不能传递null?你当然可以调用Files.createTempDirectory(null),这正如人们所期望的那样工作 - 它创建一个新的任意命名的目录并返回其路径。 - dimo414
显示剩余4条评论

24
你可以将Apache Commons IO依赖项添加到你的项目中,然后使用FileUtils.deleteDirectory()来做一些操作:
Runtime.getRuntime().addShutdownHook(new Thread() {
    @Override
    public void run() {
        try {
            FileUtils.deleteDirectory(tmp_dir_path.toFile());
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
});

有关Apache Commons的更多信息,请查看:https://commons.apache.org/proper/commons-io/


3
值得注意的是,无论目录是否为空,此操作都会删除目录。 - Joshua Richardson

2

1
不,它不会自动执行。你需要自己完成。或者使用java.io.File.deleteOnExit(),但我猜它对非空目录无效。

3
如果new File(Files.createTempDirectory(null).toString()).deleteOnExit()可以工作的话,我会更喜欢Java,哈哈哈。 - user972946
4
它的功能与文档中所述完全一致 - “目录必须为空才能被删除” - 希望删除非空目录是合理的,但不要混淆您的愿望与“Java无法运行”的说法。 - dimo414

0
如果使用JUnit 5,@TempDir注释非常好用。它还会在测试或测试类结束时(取决于注释的作用域)递归删除创建的目录。
例如:
@BeforeAll
public static void setup(@TempDir Path tempDir) {
    // The dir will be deleted when the class ends, ala @AfterClass
}

或者

@Test
public static void myTest(@TempDir Path tempDir) {
    // The dir will be deleted when this test method ends
}

请参见:https://www.baeldung.com/junit-5-temporary-directory


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接