2016-03-14 5 views
24

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

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(); 
} 

Источник: here

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

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

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

+0

* Это чувствует себя ужасно неуклюжим и многословным * Почему ? Это очень хороший способ сделать это. И Java 8 'Files.walk' не даст вам возможности сделать это. – Tunaki

+1

Потому что это заставляет пользователя перерисовать простое рекурсивное удаление ... Потому что для этого нужны 15 строк кода ... Как насчет чего-то типа 'Files.deleteRecursively (Path)' или, возможно, какой-то необязательный флаг? – fgysin

+0

Ответ заключается в том, что он просто не существует во встроенном NIO.2. У вас может быть рекурсивный подход с 'Files.list', но это то же самое, и я бы предпочел решение, которое у вас есть. – Tunaki

ответ

68

Вы можете комбинировать NIO 2 и поток API.

Path rootPath = Paths.get("/data/to-delete"); 
Files.walk(rootPath, FileVisitOption.FOLLOW_LINKS) 
    .sorted(Comparator.reverseOrder()) 
    .map(Path::toFile) 
    .peek(System.out::println) 
    .forEach(File::delete); 
  • Files.walk - возвращает все файлы/каталоги ниже rootPath включая
  • .sorted - сортировать список в обратном порядке, поэтому сам каталог приходит после того, включая подкаталоги и файлы
  • .map - отображающих Path до File
  • .peek - есть только для того, чтобы показать, какая запись обрабатывается
  • .forEach - вызывает .delete() метод на каждом объекте File

EDIT

Вот некоторые цифры.
В каталоге /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 

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

EDIT

Некоторые ДОПОЛНИТЕЛЬНО информация о Шарлем опции 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 не будет удален.

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

+0

Вы проверили это? Логика сортировки должна быть более сложной, чем сортировка строк. Кроме того, я уверен, что это очень плохая производительность: сортировка в обратном порядке требует загрузки всего потока в памяти. Если дерево каталогов глубокое, это может быть довольно проблематичным. – Tunaki

+0

@Tunaki Это было закончено для дерева каталогов '> 3.600' и'> 21.000' файлов менее чем за 10 секунд (включая время компиляции фрагмента). И имена сортируются в натуральном порядке. Поскольку имя каталога всегда короче, чем содержащийся файл, это будет после файла в обратном порядке. Если производительность, основанная на огромном дереве каталогов с огромным количеством файлов, вызывает озабоченность, вы можете удалить сначала все файлы, а затем все каталоги. В таком случае порядок не имеет значения. – SubOptimal

+0

@Tunaki Я сделал быстрый тест, сначала удалив все файлы, а затем все каталоги -> результат занимает почти в два раза больше времени. Потому что вам нужно проходить по всем каталогам. – SubOptimal

3

следующее решение не требует преобразования из файла Путь к объектам:

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); 
} 
0

Если вы должны использовать только 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; 
    } 
}); 
Смежные вопросы