62

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...

fgysin
  • 11,329
  • 13
  • 61
  • 94
  • *This feels horribly clumsy and verbose* Why? This is a very good way to do it. And Java 8 `Files.walk` won't give you the opportunity to do that. – Tunaki Mar 14 '16 at 13:04
  • 3
    Because this forces the user to redfine a simple recursive deletion... Because this needs 15 lines of code... How about something like `Files.deleteRecursively(Path)`, or maybe some optional flag? – fgysin Mar 14 '16 at 13:13
  • The answer is that it simply doesn't exist in built-in NIO.2. You could have a recursive approach with `Files.list` but it's the same and I'd prefer the solution you have. – Tunaki Mar 14 '16 at 13:14
  • 3
    @fgysin Kotlin has this [function](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-file/delete-recursively.html) in its stdlib. There is really no reason not to include it. – KeksArmee Aug 26 '17 at 02:57
  • 2
    @KeksArmee except that the Kotlin function will _always_ follow symlinks. – FutureShocked Feb 21 '19 at 22:01

6 Answers6

147

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 object

EDIT 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.

Joe White
  • 94,807
  • 60
  • 220
  • 330
SubOptimal
  • 22,518
  • 3
  • 53
  • 69
  • Did you test this? The sorting logic should be more complicated than sorting Strings. Also, I'm pretty sure this is very bad performance wise: sorting in reverse order requires to load whole the Stream in memory. If the directory tree is deep, this can be quite problematic. – Tunaki Mar 14 '16 at 13:46
  • @Tunaki It was finished for a directory tree of `> 3.600` directories and `> 21.000` files in less then 10 seconds (including the compile time of the snippet). And the names are sorted in natural order. As the directory name is always shorter then contained file, it will be after the file in reverse order. If performance based on a huge directory tree with a huge amount of files is a concern you could remove first all files and then all directories. In that case the order would not matter. – SubOptimal Mar 14 '16 at 14:07
  • 1
    @Tunaki I did a quick test with first removing all files and then all directories --> result it takes nearly twice the time. Because you have to traverse twice over all directories. – SubOptimal Mar 14 '16 at 14:15
  • It would be interesting to compare the time taken with your answer and the Java 7 snippet in the question on deep structure. – Tunaki Mar 14 '16 at 14:17
  • 1
    @Tunaki Have a look the my updated answer. I added some figures. – SubOptimal Mar 15 '16 at 06:59
  • 3
    Why the hell did you add the `FileVisitOption.FOLLOW_LINKS` flag? This means that you'll empty the whole target trees pointed-to by symlinks. – Sasha Nov 20 '16 at 02:24
  • @Sasha I allways assume people first try to understand the code they execute. But thanks for the hint I added some note about this options. – SubOptimal Nov 21 '16 at 11:07
  • 1
    @SubOptimal, sorry for a bit rude comment, but IMHO following links is just redundant here. It simply doesn't fit the task specified by topic-starter. – Sasha Nov 21 '16 at 11:37
  • 1
    So, the problem is not in that somebody copies code without understanding what it does — but in that the code is wrong (does some additional things neither asked by topic-starter, nor logically related to what topic-starter asked). How `I always assume people first try to understand the code they execute` is related to this issue? Do you mean that `FileVisitOption.FOLLOW_LINKS` was included intentionally to "punish" somebody who's blindly copy-pasting the code? – Sasha Nov 30 '16 at 17:57
  • @Sasha No it was a mistake by copy and paste, when I tested different behavior of the code locally. It was not intentionally left there. – SubOptimal Dec 01 '16 at 07:04
  • 7
    You don't need the `.map(Path::toFile)`; the arg to `.forEach` can be `Files.delete` instead. – pdxleif Apr 13 '17 at 01:05
  • 7
    @pdxleif Then you need to handle the `IOException` of `Files.delete(Path p)` inside the consumer function you pass to `forEach(...)`. Whereas `file.delete()` don't throw one. – SubOptimal Apr 13 '17 at 06:02
  • Oh, sorry - I was using Scala, which doesn't have checked exceptions, so I didn't notice that. – pdxleif Apr 14 '17 at 22:05
  • "No it was a mistake by copy and paste"... so why is it still there? – user1122069 May 20 '18 at 18:29
  • @user1122069 Seems there are some more `copy and paste`-coders. I put the option in comment and added some more hints. – SubOptimal May 22 '18 at 06:01
  • 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); – user1122069 May 22 '18 at 23:27
  • 1
    This does not close the stream, so the directory itself can be blocked and in an inconsistent state. – Seby Jun 20 '18 at 09:31
  • 1
    @Seby Not sure what you mean. `forEach()` is a terminal operation. From the [Stream javadoc](https://docs.oracle.com/javase/9/docs/api/java/util/stream/package-summary.html#StreamOps) `After the terminal operation is performed, the stream pipeline is considered consumed, and can no longer be used;`. Which resource you mean would be still in access? – SubOptimal Jun 20 '18 at 12:01
  • 1
    @SubOptimal but mapping to File you lose information about the file system, thus letting your routine fail if Path was created by e.g. JimFS. I'd rather define a static utility method in some utility class: `static public void myDelete(final Path path) { try { File.deleteIfExists(path); } catch (final IOException e) { throw new UncheckedIOException(e); } }` – Amadán Aug 13 '18 at 08:01
  • @Amadán Your comment is valid for this special case. But the OP was already using `Files.delete` and did not mention to look for a solution able to work on in-memory file systems. Neither my answer claims to be the one and only solution for all possible requirements. – SubOptimal Sep 24 '18 at 05:52
  • Isn't this just taking the ```nio.Path``` converting it to ```io.File``` and calling ```delete()```? If so how is this better? Shouldn't we have an answer for this using ```Files.delete(Path p)```? – Michael Puckett II Jan 04 '19 at 17:52
  • 1
    @MichaelPuckettII You are right with your assumption, it calls `file.delete()`. For the other have a look in the above comments - `Files.delete(path)` throws an exception, whereas `file.delete()` doesn't. Therefore the mapping from `Path` to `File`. – SubOptimal Jan 07 '19 at 07:49
  • 1
    @SubOptimal - From the [Files.walk documentation](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#walk-java.nio.file.Path-java.nio.file.FileVisitOption...-) `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.` See https://stackoverflow.com/questions/43067269/java-8-path-stream-and-filesystemexception-too-many-open-files – John Dough Oct 30 '19 at 15:41
  • @JohnDough / @Seby I updated the answer for the use of `try-with-resource`. Thank you for making this point. – SubOptimal Oct 31 '19 at 07:42
15

If you already have Spring Core as part of your project, here is an easy way to do it:

FileSystemUtils.deleteRecursively(dir);

Source:http://www.baeldung.com/java-delete-directory

justanother
  • 195
  • 1
  • 4
9

The following solution doesn't need the conversion from Path to File objects:

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);
}
asmaier
  • 11,132
  • 11
  • 76
  • 103
  • Any certain reason why you're collecting to a list and the iterating that list instead of just terminating the stream with a forEach? – Amadán Sep 24 '18 at 13:09
  • 1
    I think I did that because that way it is easier to catch exceptions during file deletion. – asmaier Sep 24 '18 at 15:28
  • 1
    I see. Checked exceptions sucks. ;-) Alternatively one might wrap the Files.deleteIfExists() in an own method and treat the exceptions there. Question is: what else but creating an unchecked exception would you want to do in this case, anyway? :-) – Amadán Sep 26 '18 at 08:17
  • 1
    Checked exceptions donot suck, if it had thrown a runtime one then you wouldn't even be aware that this `stream` can fail mid-way and stop processing -- it doesn't just ignore it, checked or unchecked, it just fails mid-way. – john16384 Apr 04 '19 at 09:09
  • 1
    You could get iterable from stream without collecting; `Iterable paths = Files.walk(...).sorted(...)::iterator; for (var path: paths) { ... }` – Coderino Javarino Apr 10 '22 at 11:34
5

If you must use only Java 7 with 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;
  }
});
bigspawn
  • 1,727
  • 3
  • 16
  • 16
  • 1
    This is the only way if you work on a virtual file systems e.g., Zip: `FileSystems.newFileSystem("jar:file:my.zip", ...).getPath("path")` - this path could only be deleted using NIO only methods. – AlexV Jun 22 '18 at 05:52
0
Files.walk(pathToBeDeleted).sorted(Comparator.reverseOrder()).forEach(Files::delete);

You'll need the "try with resources" pattern to close the stream if "timely disposal of file system resources is required".

Also, probably an unwelcome comment, but it would be much cleaner and more readable to use a library. With the code in a shared function, it won't take up much space. Every person who looks at your code must validate that this code does a proper delete, and its by no means obvious.

user1122069
  • 1,767
  • 1
  • 24
  • 52
  • 1
    This does not close the stream, so the directory itself can be blocked and in an inconsistent state. – Seby Jun 20 '18 at 09:31
  • 1
    Mapping Path to File has a bad smell to me. Omit the .map() and terminate with a .forEach(Files::delete) instead! Mind the plural of the Files utility class here! – Amadán Sep 24 '18 at 13:06
  • 1
    @Amadán Updated it. – user1122069 Feb 08 '19 at 20:03
  • 2
    Unfortunately, @Amadán is incorrect, and you can't call `Files::delete` like this, as it throws checked exceptions, so your answer now won't work. – john16384 Apr 04 '19 at 09:11
  • Darn it. Whoever invented checked exceptions hopefully already burns in hell. This could be overcome with by introducing a utility method catching the exception and wrapping it in a RuntimeException. – Amadán Apr 04 '19 at 10:53
  • @john16384 So does Files.walk. The checked exception can't be avoided without using a library function, which everyone should do, generally. – user1122069 Apr 17 '19 at 11:00
  • @user1122069 `Files.walk` however throws this in the normal way, so you can just propagate it up; you can't do this with `Files::delete` as during streaming. – john16384 Apr 17 '19 at 14:02
  • @Amadán I use a utility for this, called `Exceptional`, which works like `Optional`, but also keeps track of an exception (and offers fluid methods to deal with or ignore them). – john16384 Apr 17 '19 at 14:05
-1

FileUtils.deleteDirectory from Apache Commons IO deletes a directory recursively.

Example:

Path pathToBeDeleted = TEMP_DIRECTORY.resolve(DIRECTORY_NAME);

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

For more information see Delete a Directory Recursively in Java.

Fernando Correia
  • 21,803
  • 13
  • 83
  • 116