Перейти к основному содержимому

Удалить каталог рекурсивно в Java

· 4 мин. чтения

1. Введение

В этой статье мы покажем, как рекурсивно удалить каталог в простой Java. Мы также рассмотрим некоторые альтернативы для удаления каталогов с помощью внешних библиотек.

2. Рекурсивное удаление каталога

В Java есть возможность удалить каталог. Однако для этого требуется, чтобы каталог был пуст. Итак, нам нужно использовать рекурсию для удаления определенного непустого каталога:

  1. Получить все содержимое каталога для удаления
  2. Удалить все дочерние элементы, не являющиеся каталогом (выход из рекурсии)
  3. Для каждого подкаталога текущего каталога начните с шага 1 (рекурсивный шаг)
  4. Удалить каталог

Давайте реализуем этот простой алгоритм:

boolean deleteDirectory(File directoryToBeDeleted) {
File[] allContents = directoryToBeDeleted.listFiles();
if (allContents != null) {
for (File file : allContents) {
deleteDirectory(file);
}
}
return directoryToBeDeleted.delete();
}

Этот метод можно протестировать с помощью простого тестового примера:

@Test
public void givenDirectory_whenDeletedWithRecursion_thenIsGone()
throws IOException {

Path pathToBeDeleted = TEMP_DIRECTORY.resolve(DIRECTORY_NAME);

boolean result = deleteDirectory(pathToBeDeleted.toFile());

assertTrue(result);
assertFalse(
"Directory still exists",
Files.exists(pathToBeDeleted));
}

Метод @Before нашего тестового класса создает дерево каталогов с подкаталогами и файлами в расположении pathToBeDeleted, а метод @After при необходимости очищает каталог.

Далее давайте посмотрим, как мы можем добиться удаления, используя две наиболее часто используемые библиотеки — Apache commons-io и spring-core Spring Framework. Обе эти библиотеки позволяют нам удалять каталоги, используя всего одну строку кода.

3. Использование FileUtils из commons-io

Во- первых, нам нужно добавить зависимость commons-io в проект Maven:

<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>

Последнюю версию зависимости можно найти здесь .

Теперь мы можем использовать FileUtils для выполнения любых операций с файлами, включая deleteDirectory() , всего одним оператором:

FileUtils.deleteDirectory(file);

4. Использование FileSystemUtils из Spring

В качестве альтернативы мы можем добавить зависимость s pring-core к проекту Maven:

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>

Последнюю версию зависимости можно найти здесь.

Мы можем использовать метод deleteRecursively() в FileSystemUtils для выполнения удаления:

boolean result = FileSystemUtils.deleteRecursively(file);

Последние выпуски Java предлагают новые способы выполнения таких операций ввода-вывода, описанные в следующих разделах.

5. Использование NIO2 с Java 7

В Java 7 представлен совершенно новый способ выполнения операций с файлами с помощью Files . Это позволяет нам перемещаться по дереву каталогов и использовать обратные вызовы для выполнения действий.

public void whenDeletedWithNIO2WalkFileTree_thenIsGone() 
throws IOException {

Path pathToBeDeleted = TEMP_DIRECTORY.resolve(DIRECTORY_NAME);

Files.walkFileTree(pathToBeDeleted,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult postVisitDirectory(
Path dir, IOException exc) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult visitFile(
Path file, BasicFileAttributes attrs)
throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
});

assertFalse("Directory still exists",
Files.exists(pathToBeDeleted));
}

Метод Files.walkFileTree() проходит по дереву файлов и генерирует события. Нам нужно указать обратные вызовы для этих событий. Итак, в этом случае мы определим SimpleFileVisitor для выполнения следующих действий для сгенерированных событий:

  1. Посещение файла – удалить его
  2. Посещение каталога перед обработкой его записей — ничего не делать
  3. Посещение каталога после обработки его записей - удалите каталог, так как все записи в этом каталоге уже были обработаны (или удалены)
  4. Не удалось посетить файл — повторно сгенерируйте исключение IOException , вызвавшее сбой.

Пожалуйста, обратитесь к Введение в файловый API Java NIO2 для получения более подробной информации об API NIO2 для обработки файловых операций.

6. Использование NIO2 с Java 8

Начиная с Java 8, Stream API предлагает еще лучший способ удаления каталога:

@Test
public void whenDeletedWithFilesWalk_thenIsGone()
throws IOException {
Path pathToBeDeleted = TEMP_DIRECTORY.resolve(DIRECTORY_NAME);

Files.walk(pathToBeDeleted)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);

assertFalse("Directory still exists",
Files.exists(pathToBeDeleted));
}

Здесь Files.walk() возвращает поток пути , который мы сортируем в обратном порядке . Это помещает пути, обозначающие содержимое каталогов, перед самими каталогами. После этого он сопоставляет путь к файлу и удаляет каждый файл.

7. Заключение

В этом кратком руководстве мы рассмотрели различные способы удаления каталога. Пока мы видели, как использовать рекурсию для удаления, мы также рассмотрели некоторые библиотеки, NIO2, использующие события, и Java 8 Path Stream, использующий парадигму функционального программирования.

Весь исходный код и тестовые примеры для этой статьи доступны на GitHub .