Java NIO中File.deleteOnExit()的替代方案?

时间:2015-02-26 20:22:13

标签: java nio

Java IO具有File.deleteOnExit(),这是一种删除在JVM正常终止期间调用它的文件的方法。我发现这对于清理临时文件非常有用,特别是在单元测试期间。

但是,我在Java NIO的Files类中没有看到同名的方法。我知道我可以path.toFile().deleteOnExit(),但我想知道是否有使用NIO的替代方案。

还有其他选择吗?如果没有,为什么没有??

3 个答案:

答案 0 :(得分:28)

简答

您无法删除Java NIO中的任意文件,但是您可以在打开新流时使用StandardOpenOption.DELETE_ON_CLOSE,这将在流关闭后立即删除该文件,方法是调用{{1 (包括来自try-with-resources语句)或JVM终止。例如:

.close()

长答案

经过大量的挖掘后,我发现Java NIO 在退出时有办法删除,但它以与Java I / O不同的方式接近它。

首先,Files.createTempFile()的Javadoc描述了三种删除文件的方法:

  

用作工作文件 [sic]时,可能会打开生成的文件   使用DELETE_ON_CLOSE选项,以便在删除文件时删除   调用适当的close方法。或者,shutdown-hook,   或File.deleteOnExit()机制可用于删除文件   自动。

最后一个选择,Files.newOutputStream(Paths.get("Foo.tmp"), StandardOpenOption.DELETE_ON_CLOSE); 当然是一个Java I / O方法,我们试图避免这种方法。当您调用上述方法时,关闭挂钩就是幕后发生的事情。但File.deleteOnExit()选项是纯Java NIO。

Java NIO假定您只对删除实际打开的文件感兴趣,而不是删除任意文件。因此,创建新流(例如Files.newOutputStream())的方法可以选择使用多个OpenOptions,您可以在其中输入StandardOpenOption.DELETE_ON_CLOSE。这样做是在流关闭后立即删除文件(通过调用DELETE_ON_CLOSE或JVM退出)。

例如:

.close()

...将在流关闭时删除与流关联的文件,从显式调用Files.newOutputStream(Paths.get("Foo.tmp"), StandardOpenOption.DELETE_ON_CLOSE); ,将流作为try-with-resources语句的一部分关闭,或者JVM终止。

更新:在某些操作系统(如Linux)上,.close()会在创建OutputStream后立即删除。如果您只需要一个OutputStream,那可能仍然没问题。有关详细信息,请参阅DELETE_ON_CLOSE deletes files before close on Linux

因此,Java NIO在Java I / O上添加了新功能,因为您可以在关闭流时删除文件。如果这是在JVM退出期间删除的足够好的替代方法,则可以在纯Java NIO中执行此操作。如果没有,您将不得不依赖Java I / O的StandardOpenOption.DELETE_ON_CLOSE或shutdown-hook来删除文件。

答案 1 :(得分:12)

在幕后,File.deleteOnExit()只会通过shutdown hook创建Runtime.addShutdownHook()

然后,你可以用NIO做同样的事情:

Runtime.getRuntime().addShutdownHook(new Thread() {
  public void run() {
    Path path = ...;

    Files.delete(path);
  }
});

答案 2 :(得分:5)

我不会建议将StandardOpenOption.DELETE_ON_CLOSE作为File.deleteOnExit()的替代品。正如文档中提到的那样,它既不是通用目的,也不可能在琐碎的案例之外正常工作。

DELETE_ON_CLOSE,顾名思义,是designed to be used to remove a file once it's closed立即清理不再需要的资源。 Files.createTempFile()的文档同样明确,DELETE_ON_CLOSE可用于"工作文件"仅在文件打开时才需要。

Files.createTempFile()文档建议您直接编写自己的关闭挂钩或只是继续使用File.deleteOnExit()。尽管您希望使用NIO,但如果您只使用本地文件系统,那么使用File.deleteOnExit()并不存在任何问题。如果您没有使用(或者不确定您正在使用)本地文件系统,那么无法 使用File.deleteOnExit()它'很简单,可以编写自己的关闭挂钩just like what File does

public final class DeletePathsAtShutdown {
  private static LinkedHashSet<Path> files = new LinkedHashSet<>();

  static {
    Runtime.getRuntime().addShutdownHook(
        new Thread(DeletePathsAtShutdown::shutdownHook));
  }

  private static void shutdownHook() {
    LinkedHashSet<Path> local;
    synchronized {
      local = paths;
      paths = null;
    }

    ArrayList<Path> toBeDeleted = new ArrayList<>(theFiles);
    Collections.reverse(toBeDeleted);
    for (Path p : toBeDeleted) {
      try {
        Files.delete(p);
      } catch (IOException | RuntimeException e) {
        // do nothing - best-effort
      }
    }
  }

  public static synchronized void register(Path p) {
    if (paths == null) {
      throw new IllegalStateException("ShutdownHook already in progress.");
    }
    paths.add(p);
  }
}

当然,如果NIO包含一个开箱即用的类似关闭钩子可能会很好,但它的缺席是没有理由使用错误的工具来完成工作。您还可以向DeletePathsAtShutdown添加更多功能,例如remove()功能或deleting directories支持。