Previous articles in this series have covered reading files with Java, writing files, and constructing directory and file paths with the File
and Path
classes. This fourth part describes the most important directory and file operations. It answers the following questions:
- How to list all files in a directory?
- How to search for files matching specific criteria within a directory tree?
- How to find the current directory?
- How to find the user's home directory?
- How to find the temporary directory, and how to create a temporary file?
- How do I move files with Java?
- How do I rename a file?
- How do I copy a file?
- How to delete a file?
- How to create a symbolic link with Java?
The article answers all questions using the NIO.2 File API, introduced in Java 7 with JSR 203.
Directory operations
For the following directory operations, you need a Path-object representing the directory. You can construct this object using the static method Path.of()
(or, before Java 11, using Paths.get()
).
A comprehensive tutorial on constructing directory paths with Path
, Paths
, and File
can be found in the third part of this series.
How to list directory contents with Files.list()
The easiest way to list the complete contents of a directory is the Files.list()
method. It returns a Stream of Path
objects, which we simply write to System.out
in the following example:
Path currentDir = Path.of(System.getProperty("user.home"));
Files.list(currentDir).forEach(System.out::println);
Code language: Java (java)
How to search a directory recursively with Files.list()
Let's move on to a more complex case. In the following example, we want to output all regular files located in the home directory or a subdirectory of any depth thereof and whose name starts with "settings".
import java.io.*;
import java.nio.file.*;
public class FindFilesRecursivelyExample {
public static void main(String[] args) throws IOException {
Path currentDir = Path.of(System.getProperty("user.home"));
findFileRecursively(currentDir, "settings");
}
private static void findFileRecursively(
Path currentDir, String fileNamePrefix) throws IOException {
Files.list(currentDir).forEach(child -> {
if (Files.isRegularFile(child)
&& child.getFileName().toString().startsWith(fileNamePrefix)) {
System.out.println(child);
}
if (Files.isDirectory(child) ) {
try {
findFileRecursively(child, fileNamePrefix);
} catch (AccessDeniedException e) {
System.out.println("Access denied: " + child);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
});
}
}
Code language: Java (java)
You can use the methods Files.isRegularFile()
and Files.isDirectory()
to check if a file is a regular file or directory. Another file type is the symbolic link – you can recognize it with Files.isSymbolicLink()
. It is also possible that all three methods return false
, in which case the file is of type "other" (what exactly this could be is unspecified).
In the example above, we have to catch IOException
inside the lambda and wrap it with an UncheckedIOException
because the forEach
consumer of the stream must not throw a checked exception.
How to search a directory recursively with Files.walk()
You can write the previous example much shorter and more elegant – using Files.walk()
:
private static void findFileWithWalk(Path currentDir, String fileNamePrefix)
throws IOException {
Files.walk(currentDir).forEach(child -> {
if (Files.isRegularFile(child)
&& child.getFileName().toString().startsWith(fileNamePrefix)) {
System.out.println(child);
}
});
}
Code language: Java (java)
However, this variant has the disadvantage that an AccessDeniedException
cannot be caught individually as before. If such an exception occurs here, the entire Files.walk()
method terminates. If this is acceptable for your application, this way is more beautiful than the previous one.
How to search a directory recursively with Files.walkFileTree()
Another variant is Files.walkFileTree()
. This method implements the visitor pattern. It sends each file within the directory structure to a FileVisitor
, which you pass to the method. In the following example, we use the SimpleFileVisitor
class, which implements all methods of FileVisitor
. We only override the visitFile()
method:
private static void findFileWithWalkFileTree(
Path currentDir, String fileNamePrefix) throws IOException {
Files.walkFileTree(currentDir, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
if (Files.isRegularFile(file)
&& file.getFileName().toString().startsWith(fileNamePrefix)) {
System.out.println(file);
}
return FileVisitResult.CONTINUE;
}
});
}
Code language: Java (java)
The method's return value, FileVisitResult.CONTINUE
, indicates that walkFileTree()
should continue to traverse the directory tree. Other return values would be:
TERMINATE
– terminates thewalkFileTree()
method.SKIP_SIBLINGS
– skips all other files of the current directory.SKIP_SUBDIR
– skips the current directory – this return value cannot be returned byvisitFile()
, but byFileVisitor.preVisitDirectory()
.
The walkFileTree()
variant, too, would terminate processing in the case of an AccessDeniedException
. However, there is a way to prevent this. To do so, you also need to overwrite the method FileVisitor.visitFileFailed()
:
private static void findFileWithWalkFileTree(
Path currentDir, String fileNamePrefix) throws IOException {
Files.walkFileTree(currentDir, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
if (Files.isRegularFile(file)
&& file.getFileName().toString().startsWith(fileNamePrefix)) {
System.out.println(file);
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc)
throws IOException {
if (exc instanceof AccessDeniedException) {
System.out.println("Access denied: " + file);
return FileVisitResult.CONTINUE;
} else {
return super.visitFileFailed(file, exc);
}
}
});
}
Code language: Java (java)
How to search a directory with Files.find()
An alternative approach is the Files.find()
method. It expects a "matcher" as the third parameter: this is a function that has a Path
and BasicFileAttributes
as input parameters and returns a boolean
indicating whether the corresponding file should be included in the result or not.
private static void findFileWithFind(Path currentDir, String fileNamePrefix)
throws IOException {
Files.find(currentDir, Integer.MAX_VALUE,
(path, attributes) -> Files.isRegularFile(path)
&& path.getFileName().toString().startsWith(fileNamePrefix))
.forEach(System.out::println);
}
Code language: Java (java)
Again, an AccessDeniedException
would end the entire search prematurely. I don't know of any way to circumvent this with Files.find()
. If you expect subdirectories with denied access, you should either use Files.walkFileTree()
and overwrite the visitFileFailed()
method to catch the exception. Alternatively, use Files.list()
and implement the recursion yourself.
Accessing specific directories
Attention: If you are using a version older than Java 11, you must replace Path.of()
with Paths.get()
in the following code examples.
Accessing the current directory
The current directory can be found via the system property "user.dir":
Path currentDir = Path.of(System.getProperty("user.dir"));
Code language: Java (java)
Accessing the user's home directory
You can find the home directory of the current user via the system property "user.home":
Path homeDir = Path.of(System.getProperty("user.home"));
Code language: Java (java)
Accessing the temporary directory
And you can find the temporary directory via the system property "java.io.tmpdir":
Path tempDir = Path.of(System.getProperty("java.io.tmpdir"));
Code language: Java (java)
If you want to create a temporary file in the temporary directory, you do not need to access the temporary directory first. There is a shortcut for this. You will find it at the beginning of the following chapter, "File operations".
File operations
How to create a temporary file
After the last chapter concluded with accessing the temporary directory, this one starts with a shortcut to creating temporary files:
Path tempFile = Files.createTempFile("happycoders-", ".tmp");
Code language: Java (java)
The two parameters are a prefix and a suffix. The createTempFile()
method will insert a random number between them. When I run the method repeatedly on my Windows system and print the variable tempFile
to the console, I get the following output:
tempFile = C:\Users\svenw\AppData\Local\Temp\happycoders-7164892815754554616.tmp
tempFile = C:\Users\svenw\AppData\Local\Temp\happycoders-3557939636108137420.tmp
tempFile = C:\Users\svenw\AppData\Local\Temp\happycoders-16515581992479122220.tmp
tempFile = C:\Users\svenw\AppData\Local\Temp\happycoders-4078166990204004103.tmp
Code language: plaintext (plaintext)
On Linux it looks like this:
tempFile = /tmp/happycoders-6859515894563322081.tmp
tempFile = /tmp/happycoders-3688163816397144832.tmp
tempFile = /tmp/happycoders-2576679508175526427.tmp
tempFile = /tmp/happycoders-8074586277964353976.tmp
Code language: plaintext (plaintext)
It is essential to know that createTempFile()
does not only create the respective Path
object but actually creates an empty file.
How to move a file in Java
To move a file, use the method Files.move()
. The following example creates a temporary file and moves it to the home directory of the logged-on user:
Path tempFile = Files.createTempFile("happycoders-", ".tmp");
Path targetDir = Path.of(System.getProperty("user.home"));
Path target = targetDir.resolve(tempFile.getFileName());
Files.move(tempFile, target);
Code language: Java (java)
The second parameter of the move()
method must represent the target file, not the target directory! If you invoked Files.move(tempFile, targetDir)
here, you would get a FileAlreadyExistsException
. Therefore, in the example, we use the resolve()
method to concatenate the targetDir
with the name of the file to be copied.
How to move a directory including all subdirectories
You can move a directory just like a file. In the following example, we create two temporary directories and one file in the first directory. We then move the first directory into the second:
Path tempDir1 = Files.createTempDirectory("happycoders-");
Path tempDir2 = Files.createTempDirectory("happycoders-");
Path tempFile = Files.createTempFile(tempDir1, "happycoders-", ".tmp");
Path target = tempDir2.resolve(tempDir1.getFileName());
Files.move(tempDir1, target);
Code language: Java (java)
How to rename file with Java
After all, renaming a file (or a directory) is a special case of moving, with the destination directory being the same as the source directory and only the file name changing. In the following example, we rename a temporary file to "happycoders.tmp":
Path tempFile = Files.createTempFile("happycoders-", ".tmp");
Path target = tempFile.resolveSibling("happycoders.tmp");
Files.move(tempFile, target);
Code language: Java (java)
Invoking tempFile.resolveSibling("happycoders.tmp")
is a shortcut for tempFile.getParent().resolve("happycoders.tmp")
: The directory is extracted from the source file and concatenated with the new file name.
How to copy a file in Java
Copying a file is similar to renaming it. The following example copies a temporary file to the home directory:
Path tempFile = Files.createTempFile("happycoders-", ".tmp");
Path targetDir = Path.of(System.getProperty("user.home"));
Path target = targetDir.resolve(tempFile.getFileName());
Files.copy(tempFile, target);
Code language: Java (java)
This method has a significant advantage over proprietary implementations with FileInputStream
and FileOutputStream
, as they were necessary before Java 7 and the NIO.2 File API: Files.copy()
delegates the call to operating system-specific – and thus optimized – implementations.
How to delete a file in Java
You can delete a file (or a directory) with Files.delete()
:
Path tempFile = Files.createTempFile("happycoders-", ".tmp");
Files.delete(tempFile);
Code language: Java (java)
The directory on which you invoke Files.delete()
must be empty. Otherwise, the method will throw a DirectoryNotEmptyException
. You can try this with the following code:
Path tempDir = Files.createTempDirectory("happycoders-");
Path tempFile = Files.createTempFile(tempDir, "happycoders-", ".tmp");
Files.delete(tempDir);
Code language: Java (java)
First, a temporary directory is created, then a temporary file in it. Then an attempt is made to delete the (non-empty) directory.
How to create a symbolic link to a file
You can create a symbolic link with the method Files.createSymbolicLink()
. Attention: You have to specify target and source in reverse order as with all previous methods: first, the link path, then the path of the file to be linked. The following example creates a temporary file and then sets a symbolic link to the created file from the home directory.
Path tempFile = Files.createTempFile("happycoders-", ".tmp");
Path linkDir = Paths.get(System.getProperty("user.home"));
Path link = linkDir.resolve(tempFile.getFileName());
Files.createSymbolicLink(link, tempFile);
Code language: Java (java)
This example works on Linux without restrictions. On Windows, you need administrative rights to create symbolic links. If these are missing, the following exception is thrown: FileSystemException: [link]: A required privilege is not held by the client.
Summary and outlook
This fourth article in the series about files in Java introduced the most important directory and file operations.
In the next part, I will show you how to write and read structured data with DataOutputStream
and DataInputStream
.
We then move on to the following advanced topics:
- NIO channels and buffers introduced in Java 1.4, to speed up working with large files
- Memory-mapped I/O for convenient and blazing-fast file access without streams
- File locking, to access the same files in parallel – i.e., from several threads or processes – without conflict
As always, I appreciate it if you share the article or your feedback via the comment function. Would you like to be informed when the next part is published? Then click here to sign up for the HappyCoders newsletter.