web-dev-qa-db-ja.com

Java NIO?のFile.deleteOnExit()の代替

Java IO=には File.deleteOnExit() があります。これは、JVMの正常終了中に呼び出されたファイルを削除するメソッドです。特に単体テスト中の一時ファイルのクリーンアップに非常に役立ちます。

ただし、Java NIOの Files クラスに同じ名前のメソッドが表示されません。path.toFile().deleteOnExit()を実行できることを認識しています。しかし、NIOを使用する代替手段があるかどうかを知りたいです。

代替手段はありますか?そうでない場合、なぜないのですか?

34
Thunderforge

短い答え

Java NIOで任意のファイルを削除することはできませんが、新しいストリームを開くときに StandardOpenOption.DELETE_ON_CLOSE を使用できます。 .close()の呼び出し(try-with-resourcesステートメントからの呼び出しを含む)またはJVMの終了により、ストリームが閉じます。

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

ロングアンサー

たくさん掘り下げた後、Java NIOdoesが終了時に削除する方法を持っていることがわかりましたが、これは、Java I/O。

まず、 Files.createTempFile() のJavadocは、ファイルを削除する3つの方法を説明しています。

work files[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終了のいずれかによって、ストリームに関連付けられたファイルを削除します。

Update:Linuxなどの一部のオペレーティングシステムでは、OutputStreamが作成されるとすぐにStandardOpenOption.DELETE_ON_CLOSEが削除されます。必要なものが1つのOutputStreamだけであれば、それでも大丈夫かもしれません。詳細については、 DELETE_ON_CLOSEはLinuxで閉じる前にファイルを削除します を参照してください。

したがって、Java NIOは、Java I/Oに新しい機能を追加します。これにより、ストリームを閉じるときにファイルを削除できます。 JVM出口は、純粋なJava NIOで実行できます。そうでない場合は、Java I/OのFile.deleteOnExit()に依存する必要がありますまたは、ファイルを削除するためのシャットダウンフック。

29
Thunderforge

舞台裏では、File.deleteOnExit()Runtime.addShutdownHook()を介してshutdown hookを作成するだけです。

次に、NIOで同じことを実行できます。

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

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

靴磨き StandardOpenOption.DELETE_ON_CLOSEFile.deleteOnExit()の代わりに使用することはお勧めしません。ドキュメントが言及しているように、それは汎用であることを意図しておらず、些細なケース以外で正しく動作する可能性もありません。

DELETE_ON_CLOSEは、その名前が示すように、 閉じたファイルを削除するために使用するように設計されています 不要になったリソースをすぐにクリーンアップします。 Files.createTempFile() のドキュメントもこの点で同様に明確です。DELETE_ON_CLOSEは、ファイルが開いている間のみ必要な「作業ファイル」に使用できます。

Files.createTempFile()ドキュメントでは、独自のシャットダウンフックを直接記述するか、単にFile.deleteOnExit()を使用し続けることを提案しています。 NIOを使用したいという希望にもかかわらず、ローカルファイルシステムのみで作業している場合、File.deleteOnExit()を使用しても本質的に問題はありません。ローカルファイルシステムを使用していない(または使用していることが確実でない)場合、ca n'tFile.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がすぐに同様のシャットダウンフックを備えていればいいかもしれませんが、その不在は、ジョブに間違ったツールを使用する理由にはなりません。 remove()関数などの機能をDeletePathsAtShutdownに追加したり、 ディレクトリの削除 をサポートしたりすることもできます。

5
dimo414