Java NIO2ファイルAPIの紹介

1概要

この記事では、基本的なファイル操作を行うために、Javaプラットフォームの新しいI/O API - ** NIO2 - に焦点を当てます。

NIO2のファイルAPIは、Java 7に同梱されているJavaプラットフォームの主要な新しい機能分野の1つであり、特にパスAPIと共に新しいファイルシステムAPIのサブセットです。

2セットアップ

File APIを使用するようにプロジェクトを設定することは、このインポートを行うことの問題です。

import java.nio.file.** ;

この記事のコードサンプルはおそらくさまざまな環境で実行されるので、ユーザーのホームディレクトリのハンドルを取得しましょう。これはすべてのオペレーティングシステムで有効です。

private static String HOME = System.getProperty("user.home");

Files クラスは、 java.nio.file パッケージの主なエントリポイントの1つです。このクラスは、ファイルやディレクトリの読み取り、書き込み、操作のための豊富なAPIセットを提供します。 Files クラスのメソッドは Path オブジェクトのインスタンスに対して機能します。

3ファイルまたはディレクトリの確認

ファイルまたはファイルシステム上のディレクトリを表す Path インスタンスを持つことができます。指しているファイルまたはディレクトリが存在するかどうかにかかわらず、アクセス可能かどうかは、ファイル操作で確認できます。

わかりやすくするために、 file という用語を使用するときはいつでも、特に明記しない限りファイルとディレクトリの両方を参照します。

ファイルが存在するかどうかを確認するには、 exists APIを使用します。

@Test
public void givenExistentPath__whenConfirmsFileExists__thenCorrect() {
    Path p = Paths.get(HOME);

    assertTrue(Files.exists(p));
}

ファイルが存在しないことを確認するために、 notExists APIを使用します。

@Test
public void givenNonexistentPath__whenConfirmsFileNotExists__thenCorrect() {
    Path p = Paths.get(HOME + "/inexistent__file.txt");

    assertTrue(Files.notExists(p));
}

ファイルが myfile.txt のような通常のファイルか、それとも単なるディレクトリかを確認することもできます。私たちは isRegularFile APIを使用します。

@Test
public void givenDirPath__whenConfirmsNotRegularFile__thenCorrect() {
    Path p = Paths.get(HOME);

    assertFalse(Files.isRegularFile(p));
}

ファイルのアクセス権を確認するための静的メソッドもあります。ファイルが読み取り可能かどうかを確認するには、 isReadable APIを使用します。

@Test
public void givenExistentDirPath__whenConfirmsReadable__thenCorrect() {
    Path p = Paths.get(HOME);

    assertTrue(Files.isReadable(p));
}

書き込み可能かどうかを確認するには、 isWritable APIを使用します。

@Test
public void givenExistentDirPath__whenConfirmsWritable__thenCorrect() {
    Path p = Paths.get(HOME);

    assertTrue(Files.isWritable(p));
}

同様に、実行可能かどうかを確認するには

@Test
public void givenExistentDirPath__whenConfirmsExecutable__thenCorrect() {
    Path p = Paths.get(HOME);
    assertTrue(Files.isExecutable(p));
}

2つのパスがある場合、それらが両方とも基礎となるファイルシステム上の同じファイルを指しているかどうかを確認できます。

@Test
public void givenSameFilePaths__whenConfirmsIsSame__thenCorrect() {
    Path p1 = Paths.get(HOME);
    Path p2 = Paths.get(HOME);

    assertTrue(Files.isSameFile(p1, p2));
}

4ファイルを作成する

ファイルシステムAPIは、ファイルを作成するための単一行操作を提供します。

通常のファイルを作成するには、 createFile APIを使用し、作成したいファイルを表す Path オブジェクトを渡します。

パス内のすべての名前要素は、ファイル名とは別に存在しなければなりません。そうでなければ、__IOExceptionが発生します。

@Test
public void givenFilePath__whenCreatesNewFile__thenCorrect() {
    String fileName = "myfile__" + UUID.randomUUID().toString() + ".txt";
    Path p = Paths.get(HOME + "/" + fileName);
    assertFalse(Files.exists(p));

    Files.createFile(p);

    assertTrue(Files.exists(p));
}

上記のテストでは、最初にパスをチェックしたときに、パスが存在しないことが確認された後、 createFile 操作の後に存在することがわかりました。

ディレクトリを作成するには、 createDirectory APIを使用します。

@Test
public void givenDirPath__whenCreatesNewDir__thenCorrect() {
    String dirName = "myDir__" + UUID.randomUUID().toString();
    Path p = Paths.get(HOME + "/" + dirName);
    assertFalse(Files.exists(p));

    Files.createDirectory(p);

    assertTrue(Files.exists(p));
    assertFalse(Files.isRegularFile(p));
    assertTrue(Files.isDirectory(p));
}

この操作では、パス内のすべての名前要素が存在する必要があります。存在しない場合は、 IOException も返されます。

@Test(expected = NoSuchFileException.class)
public void givenDirPath__whenFailsToCreateRecursively__thenCorrect() {
    String dirName = "myDir__" + UUID.randomUUID().toString() + "/subdir";
    Path p = Paths.get(HOME + "/" + dirName);
    assertFalse(Files.exists(p));

    Files.createDirectory(p);
}

ただし、1回の呼び出しでディレクトリの階層を作成したい場合は、 createDirectories メソッドを使用します。前の操作とは異なり、パスに存在しないname要素が見つかった場合、 IOException はスローされず、最後の要素まで再帰的に作成されます。

@Test
public void givenDirPath__whenCreatesRecursively__thenCorrect() {
    Path dir = Paths.get(
      HOME + "/myDir__" + UUID.randomUUID().toString());
    Path subdir = dir.resolve("subdir");
    assertFalse(Files.exists(dir));
    assertFalse(Files.exists(subdir));

    Files.createDirectories(subdir);

    assertTrue(Files.exists(dir));
    assertTrue(Files.exists(subdir));
}

** 5一時ファイルの作成

多くのアプリケーションは、実行時にファイルシステム内に一時ファイルの証跡を作成します。その結果、ほとんどのファイルシステムには、そのようなアプリケーションによって生成された一時ファイルを格納するための専用ディレクトリがあります。

新しいファイルシステムAPIは、この目的のために特定の操作を提供します。

createTempFile APIがこの操作を実行します。パスオブジェクト、ファイルプレフィックス、およびファイルサフィックスを取ります。

@Test
public void givenFilePath__whenCreatesTempFile__thenCorrect() {
    String prefix = "log__";
    String suffix = ".txt";
    Path p = Paths.get(HOME + "/");

    Files.createTempFile(p, prefix, suffix);

    assertTrue(Files.exists(p));
}

これらのパラメーターは、この操作を必要とする要件には十分です。ただし、ファイルの特定の属性を指定する必要がある場合は、4番目の可変引数パラメーターがあります。

上記のテストでは、 HOME ディレクトリに一時ファイルを作成し、それぞれ提供されたプレフィックスとサフィックスの文字列を先頭に追加します。 log 8821081429012075286.txt__のようなファイル名になります。長い数値文字列はシステムによって生成されます。

ただし、接頭辞と接尾辞を付けないと、ファイル名には長い数値文字列とデフォルトの .tmp 拡張子のみが含まれます。

@Test
public void givenPath__whenCreatesTempFileWithDefaults__thenCorrect() {
    Path p = Paths.get(HOME + "/");

    Files.createTempFile(p, null, null);

    assertTrue(Files.exists(p));
}

上記の操作は 8600179353689423985.tmp のような名前のファイルを作成します。

最後に、パス、プレフィックス、サフィックスのいずれも指定しない場合、操作は全体を通してデフォルトを使用します。作成されたファイルのデフォルトの場所は、ファイルシステムが提供する一時ファイルディレクトリです。

@Test
public void givenNoFilePath__whenCreatesTempFileInTempDir__thenCorrect() {
    Path p = Files.createTempFile(null, null);

    assertTrue(Files.exists(p));
}

Windowsでは、これはデフォルトで C:\ Users \ user \ AppData \ Local \ Temp \ 6100927974988978748.tmp のようになります。

createTempFile の代わりに createTempDirectory を使用することで、上記の操作すべてを通常のファイルではなくディレクトリを作成するように調整できます。

6. ファイルを削除する

ファイルを削除するには、 delete APIを使用します。わかりやすくするために、次のテストでは、まずファイルがまだ存在していないことを確認し、次にファイルを作成して現在存在していることを確認し、最後に削除して存在しなくなったことを確認します。

@Test
public void givenPath__whenDeletes__thenCorrect() {
    Path p = Paths.get(HOME + "/fileToDelete.txt");
    assertFalse(Files.exists(p));
    Files.createFile(p);
    assertTrue(Files.exists(p));

    Files.delete(p);

    assertFalse(Files.exists(p));
}

ただし、ファイルがファイルシステムに存在しない場合、削除操作は IOException で失敗します。

@Test(expected = NoSuchFileException.class)
public void givenInexistentFile__whenDeleteFails__thenCorrect() {
    Path p = Paths.get(HOME + "/inexistentFile.txt");
    assertFalse(Files.exists(p));

    Files.delete(p);
}

ファイルが存在しない場合に黙って失敗する deleteIfExists を使用することでこのシナリオを回避できます。これは、複数のスレッドがこの操作を実行していて、失敗した現在のスレッドよりも早くスレッドが操作を実行したからといって失敗メッセージが欲しくない場合に重要です。

@Test
public void givenInexistentFile__whenDeleteIfExistsWorks__thenCorrect() {
    Path p = Paths.get(HOME + "/inexistentFile.txt");
    assertFalse(Files.exists(p));

    Files.deleteIfExists(p);
}

通常のファイルではなくディレクトリを扱う場合、削除操作はデフォルトでは再帰的には機能しません。そのため、ディレクトリが空でなければ、 IOException で失敗します。

@Test(expected = DirectoryNotEmptyException.class)
public void givenPath__whenFailsToDeleteNonEmptyDir__thenCorrect() {
    Path dir = Paths.get(
      HOME + "/emptyDir" + UUID.randomUUID().toString());
    Files.createDirectory(dir);
    assertTrue(Files.exists(dir));

    Path file = dir.resolve("file.txt");
    Files.createFile(file);

    Files.delete(dir);

    assertTrue(Files.exists(dir));
}

7. ファイルのコピー

copy APIを使用してファイルまたはディレクトリをコピーできます。

@Test
public void givenFilePath__whenCopiesToNewLocation__thenCorrect() {
    Path dir1 = Paths.get(
      HOME + "/firstdir__" + UUID.randomUUID().toString());
    Path dir2 = Paths.get(
      HOME + "/otherdir__" + UUID.randomUUID().toString());

    Files.createDirectory(dir1);
    Files.createDirectory(dir2);

    Path file1 = dir1.resolve("filetocopy.txt");
    Path file2 = dir2.resolve("filetocopy.txt");

    Files.createFile(file1);

    assertTrue(Files.exists(file1));
    assertFalse(Files.exists(file2));

    Files.copy(file1, file2);

    assertTrue(Files.exists(file2));
}

ターゲットファイルが存在する場合、 REPLACE EXISTING__オプションが指定されていないと、コピーは失敗します。

@Test(expected = FileAlreadyExistsException.class)
public void givenPath__whenCopyFailsDueToExistingFile__thenCorrect() {
    Path dir1 = Paths.get(
      HOME + "/firstdir__" + UUID.randomUUID().toString());
    Path dir2 = Paths.get(
      HOME + "/otherdir__" + UUID.randomUUID().toString());

    Files.createDirectory(dir1);
    Files.createDirectory(dir2);

    Path file1 = dir1.resolve("filetocopy.txt");
    Path file2 = dir2.resolve("filetocopy.txt");

    Files.createFile(file1);
    Files.createFile(file2);

    assertTrue(Files.exists(file1));
    assertTrue(Files.exists(file2));

    Files.copy(file1, file2);

    Files.copy(file1, file2, StandardCopyOption.REPLACE__EXISTING);
}

ただし、ディレクトリをコピーするとき、内容は再帰的にはコピーされません。つまり、 /baeldung /articles.db および /authors.db ファイルが含まれている場合は、 /baeldung を新しい場所にコピーすると空のディレクトリが作成されます。

8ファイルを移動する

move APIを使用してファイルまたはディレクトリを移動できます。ほとんどの点で copy 操作に似ています。コピー操作がGUIベースのシステムの copy and paste 操作に似ている場合、 move cut and paste 操作に似ています。

@Test
public void givenFilePath__whenMovesToNewLocation__thenCorrect() {
    Path dir1 = Paths.get(
      HOME + "/firstdir__" + UUID.randomUUID().toString());
    Path dir2 = Paths.get(
      HOME + "/otherdir__" + UUID.randomUUID().toString());

    Files.createDirectory(dir1);
    Files.createDirectory(dir2);

    Path file1 = dir1.resolve("filetocopy.txt");
    Path file2 = dir2.resolve("filetocopy.txt");
    Files.createFile(file1);

    assertTrue(Files.exists(file1));
    assertFalse(Files.exists(file2));

    Files.move(file1, file2);

    assertTrue(Files.exists(file2));
    assertFalse(Files.exists(file1));
}

copy 操作で行ったように REPLACE EXISTING オプションが指定されていない限り、ターゲットファイルが存在する場合、 move__操作は失敗します。

@Test(expected = FileAlreadyExistsException.class)
public void givenFilePath__whenMoveFailsDueToExistingFile__thenCorrect() {
    Path dir1 = Paths.get(
      HOME + "/firstdir__" + UUID.randomUUID().toString());
    Path dir2 = Paths.get(
      HOME + "/otherdir__" + UUID.randomUUID().toString());

    Files.createDirectory(dir1);
    Files.createDirectory(dir2);

    Path file1 = dir1.resolve("filetocopy.txt");
    Path file2 = dir2.resolve("filetocopy.txt");

    Files.createFile(file1);
    Files.createFile(file2);

    assertTrue(Files.exists(file1));
    assertTrue(Files.exists(file2));

    Files.move(file1, file2);

    Files.move(file1, file2, StandardCopyOption.REPLACE__EXISTING);

    assertTrue(Files.exists(file2));
    assertFalse(Files.exists(file1));
}

9結論

この記事では、Java 7の一部として出荷された新しいファイルシステムAPI(NIO2)のファイルAPIについて学び、重要なファイル操作のほとんどが実際に動作していることを確認しました。

この記事で使用したコードサンプルは、記事のhttps://github.com/eugenp/tutorials/tree/master/core-java-io[Githubプロジェクト]にあります。