¿Alternativa a File.deleteOnExit() en Java NIO?

7 minutos de lectura

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?

  • ¿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

Avatar de usuario de Thunderforge
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 el File.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 OpenOptionsdonde 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 para File.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 que File.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

Avatar de usuario de João Vitor Verona Biazibetti
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 OutputStreampor lo que si tiene la intención de devolver el Path 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


avatar de usuario de dimo414
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 DeletePathsAtShutdowncomo una remove() función, o soporte para la eliminación de directorios.

¿Ha sido útil esta solución?