Java IO tiene Archivo.eliminarAlSalir(), que es un método que elimina el archivo al que se llama durante la finalización normal de la JVM. Descubrí que esto es muy útil para limpiar archivos temporales, especialmente durante las pruebas unitarias.
Sin embargo, no veo un método con el mismo nombre en Java NIO archivos clase. Soy consciente de que puedo hacer path.toFile().deleteOnExit()
pero me gustaría saber si hay una alternativa usando NIO.
¿Hay alguna alternativa? Si no, ¿por qué no hay uno?
Forja del Trueno
Respuesta corta
No puede eliminar archivos arbitrarios en Java NIO, pero puede usar el StandardOpenOption.DELETE_ON_CLOSE
al abrir una nueva transmisión, que eliminará el archivo tan pronto como se cierre la transmisión, ya sea llamando .close()
(incluso desde una declaración de prueba con recursos) o la terminación de JVM. Por ejemplo:
Files.newOutputStream(Paths.get("Foo.tmp"), StandardOpenOption.DELETE_ON_CLOSE);
Respuesta larga
Después de investigar mucho, descubrí que Java NIO hace tiene una forma de eliminar al salir, pero lo aborda de una manera diferente que Java I/O.
En primer lugar, el Javadoc para Files.createTempFile()
describe tres formas de eliminar archivos:
Donde se usa como archivos de trabajo [sic]el archivo resultante se puede abrir usando el
DELETE_ON_CLOSE
para que el archivo se elimine cuando se invoque el método de cierre apropiado. Alternativamente, un apagado-ganchoo elFile.deleteOnExit()
Se puede utilizar un mecanismo para eliminar el archivo automáticamente.
La última elección, File.deleteOnExit()
es, por supuesto, un método de E/S de Java, que estamos tratando de evitar. Un gancho de apagado es lo que sucede detrás de escena cuando llama al método mencionado anteriormente. Pero el DELETE_ON_CLOSE
La opción es Java NIO puro.
En lugar de eliminar archivos arbitrarios, Java NIO asume que solo está interesado en eliminar los archivos que realmente está abriendo. Por lo tanto, los métodos que crean una nueva secuencia como Files.newOutputStream()
opcionalmente puede tomar varios OpenOptions
donde puede ingresar StandardOpenOption.DELETE_ON_CLOSE
. Lo que hace es eliminar el archivo tan pronto como se cierra la transmisión (ya sea mediante una llamada a .close()
o la salida de JVM).
Por ejemplo:
Files.newOutputStream(Paths.get("Foo.tmp"), StandardOpenOption.DELETE_ON_CLOSE);
… eliminará el archivo asociado con la transmisión cuando se cierre la transmisión, ya sea por una llamada explícita a .close()
el flujo se cierra como parte de una declaración de prueba con recursos o la JVM finaliza.
Actualizar: En algunos sistemas operativos como Linux, StandardOpenOption.DELETE_ON_CLOSE
se elimina tan pronto como se crea el OutputStream. Si todo lo que necesita es un OutputStream, todavía puede estar bien. Consulte DELETE_ON_CLOSE elimina archivos antes de cerrar en Linux para obtener más información.
Entonces, Java NIO agrega una nueva funcionalidad sobre Java I/O en la que puede eliminar un archivo al cerrar una secuencia. Si esa es una alternativa lo suficientemente buena para eliminar durante la salida de JVM, puede hacerlo en Java NIO puro. Si no, tendrá que confiar en las E/S de Java File.deleteOnExit()
o un gancho de apagado para eliminar el archivo.
-
Esta es una respuesta peligrosa;
DELETE_ON_CLOSE
no es un reemplazo directo paraFile.deleteOnExit()
porque lo obliga a mantener abierto el flujo de salida durante el tiempo que desee trabajar con el archivo. Además, el archivo se elimina inmediatamente en algunas plataformas.– daiscog
29 de marzo de 2017 a las 10:21
-
@megaflop Noté en mi respuesta que
DELETE_ON_CLOSE
funciona de manera diferente en el sentido de que “eliminará el archivo tan pronto como se cierre la transmisión”, así que pensé que ya había dejado en claro que no funciona de la misma manera queFile.deleteOnExit()
. Si está de acuerdo con que se elimine de inmediato (que en mis casos de uso, lo estaba), entonces ese es un buen reemplazo.– Forja del Trueno
29 de marzo de 2017 a las 17:28
-
Si no es lo mismo, entonces la pregunta no tiene respuesta.
– Simón Jenkins
11/03/2019 a las 14:30
João Vitor Verona Biazibetti
Entre bastidores, File.deleteOnExit()
simplemente creará un shutdown hook
a través de Runtime.addShutdownHook()
.
Luego, puedes hacer lo mismo con NIO:
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
Path path = ...;
Files.delete(path);
}
});
-
+1 Parece que en la respuesta aceptada el gancho de apagado se acopla al
OutputStream
por lo que si tiene la intención de devolver elPath
desde el método en el que se crea el archivo temporal: encontrará que su archivo temporal ha sido eliminado– dutoitns
2 de mayo de 2016 a las 8:11
dimo414
No recomendaría calzarse StandardOpenOption.DELETE_ON_CLOSE
en un reemplazo para File.deleteOnExit()
. Como menciona la documentación, no pretende ser de propósito general, ni es probable que funcione correctamente fuera de los casos triviales.
DELETE_ON_CLOSE
, como su nombre lo indica, está diseñado para eliminar un archivo una vez que se cierra para limpiar inmediatamente un recurso que ya no se necesita. la documentación para Files.createTempFile()
es igualmente claro en este punto, DELETE_ON_CLOSE
se puede usar para “archivos de trabajo” que solo se necesitan mientras el archivo está abierto.
El Files.createTempFile()
los documentos sugieren directamente escribir su propio enlace de apagado o simplemente continuar usando File.deleteOnExit()
. A pesar de su deseo de usar NIO, no hay nada intrínsecamente malo en usar File.deleteOnExit()
si solo está trabajando con el sistema de archivos local. Si no está usando (o no está seguro de estar usando) el sistema de archivos local y, por lo tanto, no poder usar File.deleteOnExit()
es lo suficientemente sencillo como para escribir su propio gancho de apagado como que File
hace:
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);
}
}
Claro, sería bueno si NIO incluyera un gancho de apagado similar listo para usar, pero su ausencia no es razón para usar la herramienta incorrecta para el trabajo. También puede agregar más funciones a DeletePathsAtShutdown
como una remove()
función, o soporte para la eliminación de directorios.
¿Qué haría la versión NIO que es diferente o mejor? (Además de dejar caer
.toFile()
de la cadena de llamadas).– Peter Lawrey
26 de febrero de 2015 a las 20:24
@Thunderforge DataInputStream.readLine() fue en realidad
@Deprecated
en 1998, todavía está allí. ¿Cuáles son los beneficios de “vivir completamente en NIO”?– Peter Lawrey
26 de febrero de 2015 a las 20:48
@PeterLawrey Soy consciente de que Java tiende a no eliminar las cosas que desaprueban (lo que me parece extraño viniendo de Python), pero algún día podrían hacerlo. En cuanto a “vivir completamente en NIO”, el código sería más fácil de entender si está completamente en NIO en lugar de usar ambos (especialmente para los desarrolladores más jóvenes que podrían haber comenzado con NIO y nunca antes usaron IO). Independientemente de si está de acuerdo o no con la justificación, todavía me gustaría saber si hay una alternativa.
– Forja del Trueno
26 de febrero de 2015 a las 20:58
@Thunderforge, el punto que estaba tratando de señalar es que en Java evitan agregar cosas a menos que haya una razón convincente para hacerlo. Cada método que agregan se considera con mucho cuidado y si todo lo que haría es lo mismo que uno existente, sospecho que no sucederá.
– Peter Lawrey
26 de febrero de 2015 a las 21:01
‘Vivir completamente en NIO’ no es un beneficio, es solo una restricción autoimpuesta arbitraria.
– usuario207421
26 de febrero de 2015 a las 21:07