I am currently trying to recursively delete a directory... Strangely enough the shortest piece of code I was able to find is the following construct, employing an ad-hoc inner class and in a visitor pattern...
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(); }
Source: here
This feels horribly clumsy and verbose, given that the new nio
APIs remove so much clutter and boilerplate...
Is there any shorter way of achieving a forced, recursive directory delete?
I'm looking for pure native Java 1.8 methods, so please don't link to external libraries...
To remove a directory and all its contents, including any subdirectories and files, use the rm command with the recursive option, -r . Directories that are removed with the rmdir command cannot be recovered, nor can directories and their contents removed with the rm -r command.
The delete() method of the File class deletes the file/directory represented by the current File object. This ListFiles() method of the File class returns an array holding the objects (abstract paths) of all the files (and directories) in the path represented by the current (File) object.
In Java, we can use the NIO Files. delete(Path) and Files. deleteIfExists(Path) to delete a file.
You can combine NIO 2 and the 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 try (Stream<Path> walk = Files.walk(rootPath)) { walk.sorted(Comparator.reverseOrder()) .map(Path::toFile) .peek(System.out::println) .forEach(File::delete); }
Files.walk
- return all files/directories below rootPath
including.sorted
- sort the list in reverse order, so the directory itself comes after the including subdirectories and files.map
- map the Path
to File
.peek
- is there only to show which entry is processed.forEach
- calls the .delete()
method on every File
objectEDIT As first mentioned by @Seby and now cited by @John Dough the Files.walk()
should be used in a try-with-resource
construct. Thanks to both.
From Files.walk javadoc
If timely disposal of file system resources is required, the try-with-resources construct should be used to ensure that the stream's close method is invoked after the stream operations are completed.
EDIT
Here are some figures.
The directory /data/to-delete
contained the unpacked rt.jar
of jdk1.8.0_73 and a recent build of activemq.
files: 36,427 dirs : 4,143 size : 514 MB
Times in milliseconds
int. SSD ext. USB3 NIO + Stream API 1,126 11,943 FileVisitor 1,362 13,561
Both version were executed without printing file names. The most limiting factor is the drive. Not the implementation.
EDIT
Some addtional information about tthe option FileVisitOption.FOLLOW_LINKS
.
Assume following file and directory structure
/data/dont-delete/bar /data/to-delete/foo /data/to-delete/dont-delete -> ../dont-delete
Using
Files.walk(rootPath, FileVisitOption.FOLLOW_LINKS)
will follow symlinks and the file /tmp/dont_delete/bar
would be deleted as well.
Using
Files.walk(rootPath)
will not follow symlinks and the file /tmp/dont_delete/bar
would not be deleted.
NOTE: Never use code as copy and paste without understanding what it does.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With