広告

Java : ディレクトリを丸ごと削除

標準APIの Files.delete は、空のディレクトリは削除できますが、中身のあるディレクトリは削除できません。
本記事では、中身のあるディレクトリを丸ごと削除する方法をご紹介します。


コード例

さっそくコード例を見てみましょう。

public void deleteAll(Path dir) throws IOException {

    Files.walkFileTree(dir, new SimpleFileVisitor<>() {
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            Files.delete(file);
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
            if (exc != null) {
                throw exc;
            }

            Files.delete(dir);
            return FileVisitResult.CONTINUE;
        }
    });
}

deleteAll メソッドのパラメータ dir に削除したいディレクトリを指定すると、その中身のファイルやサブディレクトリも含めてすべて削除します。

上記のコード例は、公式API仕様の FileVisitor の使用例を参考にしています。

注意

  • 大切なファイルを削除してしまわないように、十分に注意して動作確認は行ってください。

解説

public static void delete(Path path) throws IOException
ファイルを削除します。

標準API の Files.delete は、

  • ファイル
  • 空のディレクトリ

を削除できます。
しかし、空ではないディレクトリは削除できません。

例えば、次のようなファイル構成があったとします。
(コマンドは Windows の PowerShell で実行しています)

PS D:\java-work> tree /F
...
D:.
│  aaa.txt
│
├─dir1
└─dir2
        bbb.txt
  • aaa.txt : ファイル
  • dir1 : 空のディレクトリ
  • dir2 : 中身(bbb.txt)のある、空ではないディレクトリ

これらに対して Files.delete を実行してみましょう。

final var aaa = Path.of("D:", "java-work", "aaa.txt");
System.out.println(aaa); // D:\java-work\aaa.txt
System.out.println(Files.isRegularFile(aaa)); // true

// 削除OK
Files.delete(aaa);
System.out.println(Files.notExists(aaa)); // true

final var dir1 = Path.of("D:", "java-work", "dir1");
System.out.println(dir1); // D:\java-work\dir1
System.out.println(Files.isDirectory(dir1)); // true

// 削除OK
Files.delete(dir1);
System.out.println(Files.notExists(dir1)); // true

final var dir2 = Path.of("D:", "java-work", "dir2");
System.out.println(dir2); // D:\java-work\dir2
System.out.println(Files.isDirectory(dir2)); // true

try {
    // 削除NG
    Files.delete(dir2);
} catch (IOException e) {
    System.out.println(e);
}

// 結果
// ↓
//java.nio.file.DirectoryNotEmptyException: D:\java-work\dir2
PS D:\java-work> tree /F
...
D:.
└─dir2
        bbb.txt

aaa.txt と dir1 は削除できました。
しかし、dir2 を削除しようとすると DirectoryNotEmptyException が発生します。

そのため、ディレクトリを削除するには…
まずそのディレクトリ内のファイルおよびディレクトリをすべて削除して空にする必要があります。


public static Path walkFileTree(Path start, FileVisitor<? super Path> visitor) throws IOException
ファイル・ツリーを参照します。

次は Files.walkFileTree です。
walkFileTree は簡単にいうと、指定したパスのファイルおよびディレクトリの一覧に順にアクセスしていきます。

例えば、次のようなファイル構成があるとします。

PS D:\java-work> tree /F
...
D:.
└─target-dir
    │  aaa.txt
    │  bbb.txt
    │
    ├─dir1
    │  │  ccc.txt
    │  │  ddd.txt
    │  │
    │  └─dir1-2
    │          eee.txt
    │          fff.txt
    │
    └─dir2
            ggg.txt
            hhh.txt

Files.walkFileTree で、target-dir のファイルおよびディレクトリのすべてにアクセスしてみましょう。

アクセスするためには、FileVisitor を指定してコールバックを受け取ります。
今回は FileVisitor のサブクラスである SimpleFileVisitor を使います。

final var target = Path.of("D:", "java-work", "target-dir");
System.out.println(target); // D:\java-work\target-dir
System.out.println(Files.isDirectory(target)); // true

Files.walkFileTree(target, new SimpleFileVisitor<>() {
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
        System.out.println("visitFile          : " + file);
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
        System.out.println("postVisitDirectory : " + dir);
        return FileVisitResult.CONTINUE;
    }
});

// 結果
// ↓
//visitFile          : D:\java-work\target-dir\aaa.txt
//visitFile          : D:\java-work\target-dir\bbb.txt
//visitFile          : D:\java-work\target-dir\dir1\ccc.txt
//visitFile          : D:\java-work\target-dir\dir1\ddd.txt
//visitFile          : D:\java-work\target-dir\dir1\dir1-2\eee.txt
//visitFile          : D:\java-work\target-dir\dir1\dir1-2\fff.txt
//postVisitDirectory : D:\java-work\target-dir\dir1\dir1-2
//postVisitDirectory : D:\java-work\target-dir\dir1
//visitFile          : D:\java-work\target-dir\dir2\ggg.txt
//visitFile          : D:\java-work\target-dir\dir2\hhh.txt
//postVisitDirectory : D:\java-work\target-dir\dir2
//postVisitDirectory : D:\java-work\target-dir

FileVisitor の各種メソッドは、下記のタイミングでコールバックされます。

  • visitFile : ファイルが見つかったとき
  • postVisitDirectory : ディレクトリ内のすべてのファイルおよびディレクトリのコールバックが終わったとき

つまり、visitFile でファイルを削除していけば、postVisitDirectory が呼び出されたときは、そのディレクトリは空となります。

補足

  • Files.walkFileTree は、デフォルトではシンボリックリンクのディレクトリはたどりません。
    あまりないとは思いますが、もしシンボリックリンクの先も含めてアクセスしたい場合は、FileVisitOption.FOLLOW_LINKS を指定します。
    使用例については「Files (ファイル操作)」の記事をご参照ください。

まとめ

最後に、ちょっとしたユーティリティクラスとしてまとめてみました。

public class FileUtils {
    private FileUtils() {
    }

    public static void deleteAll(Path dir) throws IOException {
        Objects.requireNonNull(dir);

        Files.walkFileTree(dir, new SimpleFileVisitor<>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                Files.delete(file);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                if (exc != null) {
                    throw exc;
                }

                Files.delete(dir);
                return FileVisitResult.CONTINUE;
            }
        });
    }
}

下記のファイル構成で、target-dir ディレクトリを削除してみます。

// --- PowerShell ---
//PS D:\java-work> tree /F
//...
//D:.
//└─target-dir
//    │  aaa.txt
//    │  bbb.txt
//    │
//    ├─dir1
//    │  │  ccc.txt
//    │  │  ddd.txt
//    │  │
//    │  └─dir1-2
//    │          eee.txt
//    │          fff.txt
//    │
//    └─dir2
//            ggg.txt
//            hhh.txt

final var dir = Path.of("D:", "java-work", "target-dir");
System.out.println(dir); // D:\java-work\target-dir
System.out.println(Files.isDirectory(dir)); // true

FileUtils.deleteAll(dir);

System.out.println(Files.notExists(dir)); // true

// --- PowerShell ---
//PS D:\java-work> tree /F
//...
//D:.
//サブフォルダーは存在しません

無事に、指定したディレクトリを丸ごと削除できました。


関連記事

ページの先頭へ