[英]Alternative to File.deleteOnExit() in Java NIO?
Java IO具有File.deleteOnExit() ,这是一种删除在JVM正常终止期间调用它的文件的方法。 我发现这对于清理临时文件非常有用,特别是在单元测试期间。
但是,我没有在Java NIO的Files类中看到同名的方法。 我知道我可以做path.toFile().deleteOnExit()
,但我想知道是否有使用NIO的替代方案。
还有其他选择吗? 如果没有,为什么没有?
您无法删除Java NIO中的任意文件,但是您可以在打开新流时使用StandardOpenOption.DELETE_ON_CLOSE
,这将在流关闭后立即删除该文件,方法是调用.close()
(包括尝试 - with-resources语句)或JVM终止。 例如:
Files.newOutputStream(Paths.get("Foo.tmp"), StandardOpenOption.DELETE_ON_CLOSE);
经过大量的挖掘,我发现Java NIO 确实有一种在退出时删除的方法,但它以与Java I / O不同的方式处理它。
首先,Javadoc for Files.createTempFile()
描述了三种删除文件的方法:
在用作工作文件 [sic]的情况下,可以使用
DELETE_ON_CLOSE
选项打开生成的文件,以便在调用适当的close方法时删除该文件。 或者,可以使用shutdown-hook或File.deleteOnExit()
机制自动删除文件。
最后一个选择File.deleteOnExit()
当然是一个Java I / O方法,我们试图避免这种方法。 当您调用上述方法时,关闭挂钩就是幕后发生的事情。 但是DELETE_ON_CLOSE
选项是纯Java NIO。
Java NIO假定您只对删除实际打开的文件感兴趣,而不是删除任意文件。 因此,创建新流(如Files.newOutputStream()
可以选择使用多个OpenOptions
,您可以在其中输入StandardOpenOption.DELETE_ON_CLOSE
。 这样做是在流关闭后立即删除文件(通过调用.close()
或退出JVM)。
例如:
Files.newOutputStream(Paths.get("Foo.tmp"), StandardOpenOption.DELETE_ON_CLOSE);
...将在流关闭时删除与流关联的文件,从显式调用.close()
,将流作为try-with-resources语句的一部分关闭,或者JVM终止。
更新:在某些操作系统(如Linux)上,只要创建OutputStream, StandardOpenOption.DELETE_ON_CLOSE
删除。 如果您只需要一个OutputStream,那可能仍然没问题。 有关详细信息,请参阅DELETE_ON_CLOSE在Linux上关闭之前删除文件 。
因此,Java NIO在Java I / O上添加了新功能,因为您可以在关闭流时删除文件。 如果这是在JVM退出期间删除的足够好的替代方法,则可以在纯Java NIO中执行此操作。 如果没有,您将不得不依赖Java I / O的File.deleteOnExit()
或shutdown-hook来删除该文件。
在幕后, File.deleteOnExit()
将通过Runtime.addShutdownHook()
创建一个shutdown hook
。
然后,你可以用NIO做同样的事情:
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
Path path = ...;
Files.delete(path);
}
});
我不会建议使用StandardOpenOption.DELETE_ON_CLOSE
的OpenOpenOption.DELETE_ON_CLOSE来替换File.deleteOnExit()
。 正如文档中提到的那样,它既不是通用目的,也不可能在琐碎案例之外正常工作。
顾名思义, DELETE_ON_CLOSE
旨在用于在文件关闭后删除文件以立即清理不再需要的资源。 Files.createTempFile()
的文档在这一点上同样清楚, DELETE_ON_CLOSE
可用于文件打开时仅需要的“工作文件”。
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);
}
}
当然,如果NIO包含一个开箱即用的类似关闭钩子可能会很好,但它的缺席是没有理由使用错误的工具来完成工作。 您还可以向DeletePathsAtShutdown
添加更多功能,例如remove()
函数或支持删除目录 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.