web-dev-qa-db-fra.com

Alternative à File.deleteOnExit () dans Java NIO?

Java IO has File.deleteOnExit () , qui est une méthode qui supprime le fichier auquel il est appelé lors de l'arrêt normal de la JVM. J'ai trouvé ceci pour être très utile pour nettoyer les fichiers temporaires, en particulier lors des tests unitaires.

Cependant, je ne vois pas de méthode du même nom dans la classe Java NIO Files . Je sais que je peux faire path.toFile().deleteOnExit(), mais j'aimerais savoir s'il existe une alternative utilisant NIO.

Y a-t-il une alternative? Sinon, pourquoi n'y en a-t-il pas?

34
Thunderforge

Réponse courte

Vous ne pouvez pas supprimer des fichiers arbitraires dans Java NIO, mais vous pouvez utiliser le StandardOpenOption.DELETE_ON_CLOSE lors de l'ouverture d'un nouveau flux, ce qui supprimera le fichier comme dès que le flux se ferme, soit en appelant .close() (y compris à partir d'une instruction try-with-resources) soit en terminant la JVM. Par exemple:

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

Longue réponse

Après avoir beaucoup fouillé, j'ai trouvé que Java NIO a un moyen de supprimer à la sortie, mais il l'aborde d'une manière différente que Java I/O.

Tout d'abord, le Javadoc pour Files.createTempFile() décrit trois façons de supprimer des fichiers:

Lorsqu'il est utilisé comme fichiers de travail [sic], le fichier résultant peut être ouvert à l'aide de l'option DELETE_ON_CLOSE afin que le fichier soit supprimé lorsque la méthode de fermeture appropriée est invoquée. Alternativement, un mécanisme shutdown-hook , ou le mécanisme File.deleteOnExit() peut être utilisé pour supprimer le fichier automatiquement.

Le dernier choix, File.deleteOnExit() est bien sûr une méthode Java E/S, que nous essayons d'éviter. Un crochet d'arrêt est ce qui se passe en arrière-plan lorsque vous appelez la méthode susmentionnée. Mais l'option DELETE_ON_CLOSE est pure Java NIO.

Plutôt que de supprimer des fichiers arbitraires, Java NIO suppose que vous ne souhaitez supprimer que les fichiers que vous ouvrez réellement. Par conséquent, les méthodes qui créent un nouveau flux telles que Files.newOutputStream() peut éventuellement prendre plusieurs OpenOptions , où vous pouvez entrer StandardOpenOption.DELETE_ON_CLOSE . Ce que cela fait est de supprimer le fichier comme dès que le flux est fermé (soit par un appel à .close() soit à la sortie de la JVM).

Par exemple:

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

... supprimera le fichier associé au flux lorsque le flux est fermé, soit à partir d'un appel explicite à .close(), le flux étant fermé dans le cadre d'une instruction try-with-resources, soit à la fin de la JVM .

Mise à jour: Sur certains systèmes d'exploitation tels que Linux, StandardOpenOption.DELETE_ON_CLOSE Supprime dès que OutputStream est créé. Si tout ce dont vous avez besoin est un OutputStream, cela peut être correct. Voir DELETE_ON_CLOSE supprime les fichiers avant de fermer sur Linux pour plus d'informations.

Donc Java NIO ajoute de nouvelles fonctionnalités par rapport à Java E/S en ce que vous pouvez supprimer un fichier lors de la fermeture d'un flux. Si c'est une bonne alternative à la suppression pendant Sortie JVM, vous pouvez le faire en pur Java NIO. Sinon, vous devrez vous fier à Java I/O's File.deleteOnExit() ou un crochet d'arrêt pour supprimer le fichier.

29
Thunderforge

Dans les coulisses, File.deleteOnExit() va simplement créer un shutdown hook Via Runtime.addShutdownHook().

Ensuite, vous pouvez faire la même chose avec NIO:

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

    Files.delete(path);
  }
});
13
BloodShura

Je ne suggérerais pas de tailler les chaussures StandardOpenOption.DELETE_ON_CLOSE en remplacement de File.deleteOnExit(). Comme la documentation le mentionne, elle n'est ni destinée à un usage général, ni susceptible de fonctionner correctement en dehors des cas triviaux.

DELETE_ON_CLOSE, Comme son nom l'indique, est conçu pour être utilisé pour supprimer un fichier une fois qu'il est fermé pour nettoyer immédiatement une ressource dont vous n'avez plus besoin. La documentation pour Files.createTempFile() est également claire sur ce point, DELETE_ON_CLOSE Ne peut être utilisé que pour les "fichiers de travail" nécessaires uniquement lorsque le fichier est ouvert.

Les documents Files.createTempFile() suggèrent directement d'écrire votre propre hook d'arrêt ou simplement de continuer à utiliser File.deleteOnExit(). Malgré votre désir d'utiliser NIO, il n'y a rien de mal à utiliser File.deleteOnExit() si vous ne travaillez qu'avec le système de fichiers local. Si vous n'utilisez pas (ou n'êtes pas certain que vous utilisez) le système de fichiers local et donc ne peut pas utiliser File.deleteOnExit() c'est assez simple pour écrire votre propre hook d'arrêt comme ce que fait 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);
  }
}

Bien sûr, cela pourrait être bien si NIO incluait un crochet d'arrêt similaire hors de la boîte, mais son absence n'est pas une raison d'utiliser le mauvais outil pour le travail. Vous pouvez également ajouter plus de fonctionnalités à DeletePathsAtShutdown, comme une fonction remove(), ou la prise en charge de suppression de répertoires .

5
dimo414