Files.delete() and deleteIfExists()

Delete files and empty directories, choose between failing and ignoring a missing file, and remove a whole tree safely.

Signature
public static boolean deleteIfExists(Path path) throws IOException
  • Added in Java Java 7

Parameters

ParameterTypeDescription
$pathPathThe file, symbolic link or empty directory to delete. A symbolic link is deleted itself, not its target.

Return value

booleantrue if this call deleted the file; false if there was nothing to delete. Files.delete returns void.

Errors

DirectoryNotEmptyException if path is a directory that still has entries; IOException for other failures, such as missing permission. Files.delete additionally throws NoSuchFileException when the file does not exist.

Both methods delete one file, one symbolic link (not its target) or one empty directory. They differ only in how they treat a missing file:

  • Files.delete(path) returns nothing and throws NoSuchFileException if there is nothing to delete.
  • Files.deleteIfExists(path) returns true if it deleted something and false if the file was already gone.

Use delete when a missing file is a bug you want to hear about, for example deleting a record's attachment that the database says exists. Use deleteIfExists for cleanup, where the goal is that the file is gone afterwards and it does not matter who removed it. The boolean is handy for an idempotent DELETE endpoint or for logging what a cleanup job actually did.

Directories must be empty

Neither method deletes recursively. A directory with entries throws DirectoryNotEmptyException. To remove a tree, delete children before parents: walk it and delete in reverse order, or use Files.walkFileTree and delete each directory in postVisitDirectory. The visitor version reports errors as plain IOException and can be told to continue past failures.

Compared with java.io.File.delete

The old File.delete() returns false for every kind of failure: missing file, non-empty directory, permission denied, file locked on Windows. The NIO methods throw an exception that says which, which is far easier to act on from a production log.

Temporary files

A service that writes temporary files for uploads, exports or image processing should delete them in a finally block, with deleteIfExists so the cleanup itself does not fail when an earlier step never created the file. Disks in containers are small, and a slow leak of temp files fills them in days. Avoid File.deleteOnExit in long-running servers: it only acts at JVM shutdown and keeps every registered path in memory until then.

Path tmp = Files.createTempFile("export-", ".csv");
try {
    // write and upload the export
} finally {
    Files.deleteIfExists(tmp);
}

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Remove a stored attachment when its database row is deleted, returning 204 whether or not the file was still there.
  • Delete temporary upload or export files in a finally block so the container disk does not slowly fill.
  • Clear a batch job's working directory, including all its subdirectories, once the job completes.

Frequently asked questions

What is the difference between Files.delete and Files.deleteIfExists?

Files.delete throws NoSuchFileException when the file is missing. Files.deleteIfExists returns false instead, and true when it deleted something. Both throw for other problems such as a non-empty directory.

How do I delete a directory with files in it in Java?

Delete its contents first. Walk it with Files.walk, sort the paths in reverse order and delete each one, or use Files.walkFileTree and delete directories in postVisitDirectory.

Does Files.delete follow symbolic links?

No. It deletes the link itself and leaves the target untouched.

More examples

An idempotent DELETE handler

HTTP DELETE should be idempotent, so a repeated request gets the same 204 with no body, while the boolean from deleteIfExists still goes to the audit log. The id is checked first because it comes from the request and must never reach resolve unchecked.

Example · java
record Result(int status, String audit) {}

Result deleteAttachment(Path storage, String id) throws IOException {
    // The id comes from the URL, so allow only safe characters before building a path.
    if (!id.matches("[A-Za-z0-9-]{1,64}")) {
        return new Result(400, "invalid id");
    }
    boolean deleted = Files.deleteIfExists(storage.resolve(id + ".bin"));
    // Deleting something already gone is still a success for the client.
    return new Result(204, deleted ? "deleted" : "already gone");
}

void main() throws IOException {
    Path storage = Files.createTempDirectory("storage");
    try {
        Files.write(storage.resolve("a1.bin"), new byte[] {1, 2});
        IO.println(deleteAttachment(storage, "a1"));
        IO.println(deleteAttachment(storage, "a1"));
        IO.println(deleteAttachment(storage, "../a1"));
    } finally {
        Files.deleteIfExists(storage);
    }
}

// Output:
// Result[status=204, audit=deleted]
// Result[status=204, audit=already gone]
// Result[status=400, audit=invalid id]

Cleaning up a temporary file in finally

The finally block runs on success and on failure, so no export leaves a file behind in the temp directory. deleteIfExists keeps the cleanup from throwing a second exception that would hide the original one.

Example · java
String exportReport(boolean failMidway) throws IOException {
    Path tmp = Files.createTempFile("report-", ".csv");
    try {
        Files.writeString(tmp, "id,total\n1001,25.99\n");
        if (failMidway) {
            throw new IOException("upload to storage failed");
        }
        return "uploaded " + Files.size(tmp) + " bytes";
    } finally {
        Files.deleteIfExists(tmp);
    }
}

void main() {
    for (boolean fail : List.of(false, true)) {
        try {
            IO.println(exportReport(fail));
        } catch (IOException e) {
            IO.println("failed: " + e.getMessage());
        }
    }
}

// Output:
// uploaded 20 bytes
// failed: upload to storage failed

Deleting a tree with walkFileTree

postVisitDirectory runs after every entry of a directory has been visited, so the directory is empty when it is deleted. Unlike a Files.walk stream, the visitor throws plain IOException and needs no unchecked wrapping.

Example · java
void deleteTree(Path root) throws IOException {
    Files.walkFileTree(root, new SimpleFileVisitor<>() {
        @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 {
            if (exc != null) throw exc;
            Files.delete(dir); // all entries are gone by now
            return FileVisitResult.CONTINUE;
        }
    });
}

void main() throws IOException {
    Path root = Files.createTempDirectory("job-17");
    Files.createDirectories(root.resolve("work/chunks"));
    Files.writeString(root.resolve("work/chunks/part-1"), "a");
    Files.writeString(root.resolve("work/chunks/part-2"), "b");
    Files.writeString(root.resolve("result.json"), "{}");

    deleteTree(root);
    IO.println("job directory exists: " + Files.exists(root));
}

// Output:
// job directory exists: false

Discussion

  • Be the first to comment on this lesson.