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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28752006/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 14:07:35  来源:igfitidea点击:

Alternative to File.deleteOnExit() in Java NIO?

javanio

提问by Thunderforge

Java IO has File.deleteOnExit(), which is a method that deletes the file it is called on during normal termination of the JVM. I've found this to be very useful for cleaning up temporary files, especially during unit tests.

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

However, I don't see a method by the same name in Java NIO's Filesclass. I am aware that I can do path.toFile().deleteOnExit(), but I'd like to know if there is an alternative using NIO.

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

Is there an alternative? If not, why is there not one?

有替代方案吗?如果没有,为什么没有?

采纳答案by Thunderforge

Short Answer

简答

You can't delete arbitrary files in Java NIO, but you can use the StandardOpenOption.DELETE_ON_CLOSEwhen opening a new stream, which will delete the file as soon as the stream closes, either by calling .close()(including from a try-with-resources statement) or the JVM terminating. For instance:

你不能在 Java NIO 中删除任意文件,但是你可以StandardOpenOption.DELETE_ON_CLOSE在打开一个新的流时使用,它会在流关闭时立即删除文件,通过调用.close()(包括来自 try-with-resources 语句)或JVM 终止。例如:

Files.newOutputStream(Paths.get("Foo.tmp"), StandardOpenOption.DELETE_ON_CLOSE);

Long Answer

长答案

After a great deal of digging around, I found that Java NIO doeshave a way to delete on exit, but it approaches it in a different way that Java I/O.

经过大量的挖掘,我发现 Java NIO确实有一种退出时删除的方法,但它以与 Java I/O 不同的方式实现它。

First off, the Javadoc for Files.createTempFile()describes three ways to delete files:

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

Where used as a work files[sic], the resulting file may be opened using the DELETE_ON_CLOSEoption so that the file is deleted when the appropriate close method is invoked. Alternatively, a shutdown-hook, or the File.deleteOnExit()mechanism may be used to delete the file automatically.

如果用作工作文件[原文如此],则可以使用该DELETE_ON_CLOSE选项打开生成的文件,以便在调用适当的关闭方法时删除该文件。或者,可以使用shutdown-hook或该File.deleteOnExit()机制自动删除文件。

The last choice, File.deleteOnExit()is of course a Java I/O method, which we are trying to avoid. A shutdown-hook is what is happening behind the scenes when you call the aforementioned method. But the DELETE_ON_CLOSEoption is pure Java NIO.

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

Rather than deleting arbitrary files, Java NIO assumes that you are only interested in deleting files that you are actually opening. Therefore, methods that create a new stream such as Files.newOutputStream()can optionally take several OpenOptions, where you can input StandardOpenOption.DELETE_ON_CLOSE. What that does is delete the file as soon as the stream is closed (either by a call to .close()or the JVM exiting).

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

For instance:

例如:

Files.newOutputStream(Paths.get("Foo.tmp"), StandardOpenOption.DELETE_ON_CLOSE);

...will delete the file associated with the stream when the stream is closed, either from an explicit call to .close(), the stream being closed as part of a try-with-resources statement, or the JVM terminating.

...将在流关闭时删除与流关联的文件,无论是通过显式调用.close(),流作为 try-with-resources 语句的一部分关闭,还是 JVM 终止。

Update:On some operating systems such as Linux, StandardOpenOption.DELETE_ON_CLOSEdeletes as soon as the OutputStream is created. If all you need is one OutputStream, that may still be okay. See DELETE_ON_CLOSE deletes files before close on Linuxfor more info.

更新:在某些操作系统(例如 Linux)上,StandardOpenOption.DELETE_ON_CLOSE在创建 OutputStream 后立即删除。如果您只需要一个 OutputStream,那可能还是可以的。有关详细信息,请参阅DELETE_ON_CLOSE 在 Linux 上关闭之前删除文件

So Java NIO adds new functionality over Java I/O in that you can delete a file when closing a stream. If that's a good enough alternative to deleting during JVM exit, you can do it in pure Java NIO. If not, you'll have to rely on Java I/O's File.deleteOnExit()or a shutdown-hook to delete the file.

因此,Java NIO 在 Java I/O 上添加了新功能,您可以在关闭流时删除文件。如果这是在 JVM 退出期间删除的一个足够好的替代方法,您可以在纯 Java NIO 中进行。如果没有,您将不得不依靠 Java I/OFile.deleteOnExit()或关闭挂钩来删除文件。

回答by BloodShura

Behind the scenes, File.deleteOnExit()will just create a shutdown hookvia Runtime.addShutdownHook().

在幕后,File.deleteOnExit()只会创建一个shutdown hookvia Runtime.addShutdownHook()

Then, you can do the same thing with NIO:

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

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

    Files.delete(path);
  }
});

回答by dimo414

I would not suggest shoe-horning StandardOpenOption.DELETE_ON_CLOSEinto a replacement for File.deleteOnExit(). As the documentation mentions it's neither intended to be general-purpose, nor is it likely to work correctly outside of trivial cases.

我不建议硬着头皮StandardOpenOption.DELETE_ON_CLOSE代替File.deleteOnExit(). 正如文档中提到的,它既不是通用的,也不是在微不足道的情况下正常工作的。

DELETE_ON_CLOSE, as the name implies, is designed to be used to remove a file once it's closedto immediately clean up a no-longer-needed resource. The documentation for Files.createTempFile()is similarly clear on this point, DELETE_ON_CLOSEcan be used for "work files" only needed while the file is open.

DELETE_ON_CLOSE顾名思义,旨在用于删除关闭的文件,以立即清理不再需要的资源。的文档Files.createTempFile()在这一点上同样清晰,DELETE_ON_CLOSE可用于仅在文件打开时需要的“工作文件”。

The Files.createTempFile()docs suggest directly either writing your own shutdown hook or simply continuing to use File.deleteOnExit(). Despite your desire to use NIO there's nothing inherently wrong with using File.deleteOnExit()if you're only working with the local filesystem. If you aren't using (or aren't certain you're using) the local filesystem and therefore can'tuse File.deleteOnExit()it's straightforward enough to write your own shutdown hook just like what Filedoes:

Files.createTempFile()文件建议或者直接写自己的关闭钩子或者干脆继续使用File.deleteOnExit()。尽管您希望使用 NIO,但File.deleteOnExit()如果您只使用本地文件系统,那么使用它并没有本质上的错误。如果您没有使用(或不确定您正在使用)本地文件系统,因此无法使用File.deleteOnExit()它,那么编写您自己的关闭钩子File足够简单了,就像这样

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);
  }
}

Sure, it might be nice if NIO included a similar shutdown hook out of the box, but its absence is no reason to use the wrong tool for the job. You can also add more functionality to DeletePathsAtShutdown, such as a remove()function, or support for deleting directories.

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