Java basics - Working with Files

Joe • updated : June 10, 2020

1. Overview

A file system stores and organizes files on some form of media, typically in a tree (or hierarchical) structure.  The root nodes are at the top of the tree and contain files and directories (folders in Windows). Each directory can contain files and subdirectories, which in turn can contain files and subdirectories etc.

A file is identified by its path through the file system beginning from the root node. The delimiter (forward (/) or backslash (\) slash is specific to the file system. 

/home/joe/hello.txt or C:\home\joe\hello.txt for Windows.

1.1 Path Interface -

A Path is a sequence of directory names, optionally followed by a file name. A Path could be relative or absolute. A Path that starts with a root (/ or C:\) component is absolute; otherwise, it's relative. A relative path needs to be combined with another path in order to access a file. 

Path absolute = Paths.get("/", "home","hello");
Path relative = Paths.get("myApp", "config","app.properties");


A Path Object is merely an abstract sequence of names and does not have to correspond to a file that actually exists. To create a file, we have to make a path and then call the relevant method to create the file. 

To create a Path, we can use the Paths class which consists of static methods that returns a Path by converting a path string or URI.

For a given Path Path dir = Paths.get(System.getProperty("user.home"),"logs","public", "test.txt");

Assuming the user home is /Users/joseph/ ,  the above example will create a path such as /Users/joseph/logs/public/test.txt in a UNIX OS.

2. Creating Files and Directories 

The Files class of the java.nio.file package offers a rich set of static methods for reading, writing, and manipulating files and directories.

We can use the Files.createDirectory(dir) method to create a directory.  The newly created directory will have default attributes if we don't specify any FileAttributes. All but the last component in the path must already exist. 

However, to create a directory with several levels deep when one or more of the parent directories might not yet exists, then we can use the Files.createDirectories(dir) method.

public static void createDirectoriesWithPermission(Path directories, String permission) throws IOException {
        if (Objects.isNull(permission)) {
            permission = DEFAULT_PERMISSION;
        }
        FileAttribute> attr = fileAttributes(permission);
        Files.createDirectories(directories, attr);
}

 

Similarly, we can create a regular file using the Files.createFile(Path, FileAttribute) method.

This method throws the FileAlreadyExistsException - if a file of that name already exists.
 

    public static File createRegularFile(Path path) throws IOException {

        if (Files.notExists(path, LinkOption.NOFOLLOW_LINKS)) {
            return Files.createFile(path, fileAttributes(DEFAULT_PERMISSION)).toFile();
        }
        return path.toFile();

    }

3. Listing Files and Directory’s content 

The File I/O method provides various ways of listing the content of a directory. The static Files.list method returns a Stream that reads the entries of a directory; however, this method does not enter subdirectories.  To process all the descendants of a directory (files, subdirectories, hidden files), we can use the Files.walk or Files.newDirectoryStream(Path)   method.

    public static List listFiles(Path pathToDir) {
        try ( Stream files = Files.list(pathToDir)) {
            return files
                    .filter(Files::isRegularFile)
                    .collect(Collectors.toList());
        } catch (IOException ex) {
            log.error(ex.getMessage());
        }
        return List.of();
    }

Furthermore, if we want to fetch only files and subdirectories where each name matches a particular pattern, we can do so by using the Files.newDirectoryStream(Path, String) method, which provides a built-in glob filter.

    public static List listFilesByExtension(Path dir, String filename) {
        //filename can be any file extension e.g "*.{java,class,json,log,txt}"
        try ( DirectoryStream dirStream = Files.newDirectoryStream(dir, filename)) {

            return StreamSupport
                    .stream(dirStream.spliterator(), false)
                    .collect(Collectors.toList());

        } catch (IOException | DirectoryIteratorException x) {
            log.error(x.getMessage());
        }
        return List.of();
    }

4. Read and Write Files

The File I/O method offers a wide range of options to read, write and open files. The code snippet below shows how to write and read a small file.

    public static void writeTextToFile(Path path, String text) throws IOException {
        Files.write(path, singletonList(text), StandardOpenOption.CREATE);
    }

    public static List readFromFile(Path path) throws IOException {
        return Files.readAllLines(path);
    }

5. Copy or Move files

To copy or move a file from one location to another, we use the Files.copy(fromPath,toPath) and Files.move(fromPath,toPath) method respectively.

    public static void moveFile(Path source, Path destination) throws IOException {
        Files.move(source, destination,
                StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);

    }


it is important to note that the copy or move will fail if the target exists. To overwrite the existing target, we use the REPLACE_EXISTING option. similarly, we can use the COPY_ATTRIBUTES option to copy all existing file attributes.
we can specify that a move should be atomic using the ATOMIC_MOVE option. This implies that either the move completes successfully, or the source file continues to be present.

The Files class also have provisions for copying between files and streams as shown in the example below where all bytes from the input stream (web page) is downloaded to a file. 
 

    public static void downloadWebPage(@NonNull String webPage, Path destination) throws IOException {

        URI uri = URI.create(webPage);
        try ( InputStream in = uri.toURL().openStream()) {
            Files.copy(in, destination, StandardCopyOption.REPLACE_EXISTING);
        }

    }

6. Deleting a File or Directory
 

we can also delete the created file using the Files.delete(path);
However, this method throws an exception if the file does not exist. Preferably, we may want to use the deleteIfExists(path) method as it does not throw any exception if the file does not exist. 
 

    public static void deleteFilesAndLinks(Path path) throws IOException {

        Files.walk(path, FileVisitOption.FOLLOW_LINKS)
                .map(Path::toFile)
                .forEach(File::delete);

    }

In conclusion, the File I/O class offers other very useful methods e.g Files.walkFileTree(..) which are not covered here, please see the Java SE official website to learn more.
Below is the unit tests for some of the file operations we have discussed.

7. FileUtil Unit Test

 /**
     * Test of all methods, of class FileUtil.
     *
     * @param tempDir
     * @throws java.io.IOException
     */
    @Test
    public void testFileUtilMethods(@TempDir Path tempDir) throws IOException {

        Path dev = tempDir.resolve("dev");
        Path prod = tempDir.resolve("prod");
        Path subDir = dev.resolve("subDir");

        //create dir
        FileUtil.createDirectoriesWithPermission(dev, "rwxr--r--");
        FileUtil.createDirectoriesWithPermission(subDir, null);

        assertAll(
                () -> assertTrue(Files.isDirectory(dev)),
                () -> assertTrue(Files.isDirectory(subDir)),
                () -> assertTrue(Files.notExists(prod)));

        //create regular files
        File readmeFile = FileUtil.createRegularFile(dev.resolve("README.md"));
        File logFile = FileUtil.createRegularFile(dev.resolve("sys.log"));
        File subDirFile = FileUtil.createRegularFile(subDir.resolve("subDir.log"));

        assertAll(
                () -> assertTrue(Files.isRegularFile(readmeFile.toPath())),
                () -> assertTrue(Files.isRegularFile(subDirFile.toPath())),
                () -> assertTrue(Files.isRegularFile(logFile.toPath())));

        //write to file
        String text = "This is a readme file";
        FileUtil.writeTextToFile(dev.resolve("README.md"), text);

        assertLinesMatch(singletonList("This is a readme file"),
                Files.readAllLines(dev.resolve("README.md")));
        assertLinesMatch(singletonList("This is a readme file"),
                FileUtil.readFromFile(dev.resolve("README.md")));

        //copy files
        FileUtil.downloadWebPage("https://www.computingfacts.com/",
                dev.resolve("web.html"));

        assertTrue(Files.isRegularFile(dev.resolve("web.html")));

        //list files by file extension
        List filesByExtension = FileUtil.listFilesByExtension(dev, "*.{md,html}");

        assertThat(filesByExtension, hasSize(2));

        //list all files
        List dirs = FileUtil.listFiles(dev);

        assertThat(dirs, hasSize(3));

        List subDirs = FileUtil.listFilesAndDirs(dev);

        assertThat(subDirs, hasSize(6));
        assertThat(subDirs, hasItem(subDir));

        assertTrue(subDirs.size() > dirs.size());

        //move files and dir
        FileUtil.moveFile(dev, prod);
        assertAll(
                () -> assertTrue(Files.isDirectory(prod)),
                () -> assertTrue(Files.notExists(dev)));

        assertLinesMatch(singletonList("This is a readme file"),
                Files.readAllLines(prod.resolve("README.md")));

        //delete files and dir
        FileUtil.deleteFilesAndLinks(tempDir);

        //validate delete
        List emptyDir = FileUtil.listFiles(tempDir);
        assertThat(emptyDir, hasSize(0));
    }



References 

The Java Tutorials 

Java

Similar Posts ..

Subscribe to our monthly newsletter. No spam, we promise !

Guest