Java NIO 中 File.deleteOnExit() 的替代方案?
Alternative to File.deleteOnExit() in Java NIO?
JavaIO有File.deleteOnExit(),是JVM正常终止时删除调用它的文件的方法。我发现这对于清理临时文件非常有用,尤其是在单元测试期间。
但是,我在 Java NIO 的 Files class 中没有看到同名方法。我知道我可以做到 path.toFile().deleteOnExit()
,但我想知道是否有使用 NIO 的替代方法。
有其他选择吗?如果没有,为什么没有?
在幕后,File.deleteOnExit()
只会通过 Runtime.addShutdownHook()
创建一个 shutdown hook
。
然后,你可以用 NIO 做同样的事情:
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
Path path = ...;
Files.delete(path);
}
});
简答
您不能在 Java NIO 中删除任意文件,但您可以在打开新流时使用 StandardOpenOption.DELETE_ON_CLOSE
,这将在流关闭后立即删除文件,或者通过调用 .close()
(包括来自 try-with-resources 语句)或 JVM 终止。例如:
Files.newOutputStream(Paths.get("Foo.tmp"), StandardOpenOption.DELETE_ON_CLOSE);
长答案
经过大量的挖掘,我发现 Java NIO 确实 有办法在退出时删除,但它以不同的方式处理它Java I/O.
首先,Files.createTempFile()
的 Java 文档描述了三种删除文件的方法:
Where used as a work files [sic], the resulting file may be opened
using the DELETE_ON_CLOSE
option 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.
最后的选择,File.deleteOnExit()
当然是JavaI/O的方法,我们尽量避免。当您调用上述方法时,shutdown-hook 是在幕后发生的事情。但是 DELETE_ON_CLOSE
选项是纯 Java NIO.
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,StandardOpenOption.DELETE_ON_CLOSE
会在创建 OutputStream 后立即删除。如果您只需要一个 OutputStream,那可能仍然没问题。有关详细信息,请参阅 DELETE_ON_CLOSE deletes files before close on Linux。
因此 Java NIO 在 Java I/O 的基础上添加了新功能,您可以在关闭流时删除文件。如果这是在 JVM 退出期间删除的足够好的替代方法,您可以在纯 Java NIO 中执行此操作。如果没有,您将不得不依靠 Java I/O 的 File.deleteOnExit()
或关闭挂钩来删除文件。
我不建议用鞋角 StandardOpenOption.DELETE_ON_CLOSE
代替 File.deleteOnExit()
。正如文档中提到的,它既不是通用的,也不太可能在琐碎的情况下正常工作。
DELETE_ON_CLOSE
,顾名思义,就是 to immediately clean up a no-longer-needed resource. The documentation for Files.createTempFile()
在这一点上同样清楚,DELETE_ON_CLOSE
可以用于"work files"只在文件打开时才需要.
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.
JavaIO有File.deleteOnExit(),是JVM正常终止时删除调用它的文件的方法。我发现这对于清理临时文件非常有用,尤其是在单元测试期间。
但是,我在 Java NIO 的 Files class 中没有看到同名方法。我知道我可以做到 path.toFile().deleteOnExit()
,但我想知道是否有使用 NIO 的替代方法。
有其他选择吗?如果没有,为什么没有?
在幕后,File.deleteOnExit()
只会通过 Runtime.addShutdownHook()
创建一个 shutdown hook
。
然后,你可以用 NIO 做同样的事情:
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
Path path = ...;
Files.delete(path);
}
});
简答
您不能在 Java NIO 中删除任意文件,但您可以在打开新流时使用 StandardOpenOption.DELETE_ON_CLOSE
,这将在流关闭后立即删除文件,或者通过调用 .close()
(包括来自 try-with-resources 语句)或 JVM 终止。例如:
Files.newOutputStream(Paths.get("Foo.tmp"), StandardOpenOption.DELETE_ON_CLOSE);
长答案
经过大量的挖掘,我发现 Java NIO 确实 有办法在退出时删除,但它以不同的方式处理它Java I/O.
首先,Files.createTempFile()
的 Java 文档描述了三种删除文件的方法:
Where used as a work files [sic], the resulting file may be opened using the
DELETE_ON_CLOSE
option so that the file is deleted when the appropriate close method is invoked. Alternatively, a shutdown-hook, or theFile.deleteOnExit()
mechanism may be used to delete the file automatically.
最后的选择,File.deleteOnExit()
当然是JavaI/O的方法,我们尽量避免。当您调用上述方法时,shutdown-hook 是在幕后发生的事情。但是 DELETE_ON_CLOSE
选项是纯 Java NIO.
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,StandardOpenOption.DELETE_ON_CLOSE
会在创建 OutputStream 后立即删除。如果您只需要一个 OutputStream,那可能仍然没问题。有关详细信息,请参阅 DELETE_ON_CLOSE deletes files before close on Linux。
因此 Java NIO 在 Java I/O 的基础上添加了新功能,您可以在关闭流时删除文件。如果这是在 JVM 退出期间删除的足够好的替代方法,您可以在纯 Java NIO 中执行此操作。如果没有,您将不得不依靠 Java I/O 的 File.deleteOnExit()
或关闭挂钩来删除文件。
我不建议用鞋角 StandardOpenOption.DELETE_ON_CLOSE
代替 File.deleteOnExit()
。正如文档中提到的,它既不是通用的,也不太可能在琐碎的情况下正常工作。
DELETE_ON_CLOSE
,顾名思义,就是Files.createTempFile()
在这一点上同样清楚,DELETE_ON_CLOSE
可以用于"work files"只在文件打开时才需要.
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.