Java.nio: наиболее сжатый рекурсивный каталог delete

В настоящее время я пытаюсь рекурсивно удалить каталог... Как ни странно, самым коротким фрагментом кода, который я смог найти, является следующая конструкция, использующая внутренний класс ad-hoc и шаблон посетителя...

Path rootPath = Paths.get("data/to-delete");

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

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
      Files.delete(dir);
      System.out.println("delete dir: " + dir.toString());
      return FileVisitResult.CONTINUE;
    }
  });
} catch(IOException e){
  e.printStackTrace();
}

Источник: здесь

Это кажется ужасно неуклюжим и многословным, учитывая, что новые API nio удаляют так много беспорядка и шаблона...

Есть ли какой-либо более короткий способ добиться принудительного рекурсивного каталога?

Я ищу чистые родные методы Java 1.8, поэтому, пожалуйста, не связывайтесь с внешними библиотеками...

Ответ 1

Вы можете объединить NIO 2 и Stream API.

Path rootPath = Paths.get("/data/to-delete");
// before you copy and paste the snippet
// - read the post till the end
// - read the javadoc to understand what the code will do 
//
// a) to follow softlinks (removes the linked file too) use
// Files.walk(rootPath, FileVisitOption.FOLLOW_LINKS)
//
// b) to not follow softlinks (removes only the softlink) use
// the snippet below
Files.walk(rootPath)
    .sorted(Comparator.reverseOrder())
    .map(Path::toFile)
    .peek(System.out::println)
    .forEach(File::delete);
  • Files.walk - вернуть все файлы/каталоги ниже rootPath включая
  • .sorted - сортировка списка в обратном порядке, поэтому сам каталог идет после .sorted подкаталогов и файлов.
  • .map - сопоставить Path к File
  • .peek - есть только, чтобы показать, какая запись обрабатывается
  • .forEach - вызывает метод .delete() для каждого объекта File

РЕДАКТИРОВАТЬ

Вот некоторые цифры.
Каталог /data/to-delete содержит распакованный rt.jar из jdk1.8.0_73 и последнюю сборку activemq.

files: 36,427
dirs :  4,143
size : 514 MB

Раз в миллисекундах

                    int. SSD     ext. USB3
NIO + Stream API    1,126        11,943
FileVisitor         1,362        13,561

Обе версии были выполнены без печати имен файлов. Наиболее ограничивающим фактором является драйв. Не реализация.

РЕДАКТИРОВАТЬ

Некоторая дополнительная информация о опции FileVisitOption.FOLLOW_LINKS.

Предположим, следующая структура файлов и каталогов

/data/dont-delete/bar
/data/to-delete/foo
/data/to-delete/dont-delete -> ../dont-delete

С помощью

Files.walk(rootPath, FileVisitOption.FOLLOW_LINKS)

будет следовать символическим ссылкам, и файл /tmp/dont_delete/bar будет также удален.

С помощью

Files.walk(rootPath)

не будет следовать символическим ссылкам, и файл /tmp/dont_delete/bar не будет удален.

ПРИМЕЧАНИЕ. Никогда не используйте код для копирования и вставки, не понимая, что он делает.

Ответ 2

Следующее решение не нуждается в преобразовании из объектов Path to File:

Path rootPath = Paths.get("/data/to-delete");     
final List<Path> pathsToDelete = Files.walk(rootPath).sorted(Comparator.reverseOrder()).collect(Collectors.toList());
for(Path path : pathsToDelete) {
    Files.deleteIfExists(path);
}

Ответ 3

Если вы должны использовать только Java 7 с NIO

Path path = Paths.get("./target/logs");
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
  @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 {
    Files.delete(dir);
    return FileVisitResult.CONTINUE;
  }
});

Ответ 4

Если у вас уже есть Spring Core как часть вашего проекта, вот простой способ сделать это:

FileSystemUtils.deleteRecursively(file);

Источник: http://www.baeldung.com/java-delete-directory

Ответ 5

Files.walk(pathToBeDeleted).sorted(Comparator.reverseOrder()).forEach(Files::delete);

Вам понадобится шаблон "попробовать с ресурсами", чтобы закрыть поток, если "требуется своевременное удаление ресурсов файловой системы".

Также, возможно, нежелательный комментарий, но было бы намного чище и удобнее читать библиотеку. С кодом в разделяемой функции он не займет много места. Каждый, кто просматривает ваш код, должен подтвердить, что этот код правильно удаляется, и это ни в коем случае не очевидно.

Ответ 6

FileUtils.deleteDirectory из Apache Commons IO рекурсивно удаляет каталог.

Пример:

Path pathToBeDeleted = TEMP_DIRECTORY.resolve(DIRECTORY_NAME);

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

Для получения дополнительной информации см. Удаление каталога рекурсивно в Java.