Java Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Overview

In this tutorial, we're going to focus on the standard open options available for files in Java.

We'll explore the StandardOpenOption enum that implements the OpenOption interface and that defines these standard open options.

2. The OpenOption Parameter

In Java, we can work with files using the NIO2 API, which contains several utility methods. Some of these methods use an optional OpenOption parameter that configures how to open or create a file. In addition, this parameter will have a default value if not set, which can be different for each of these methods.

The StandardOpenOption enum type defines the standard options and implements the OpenOption interface.

Here's the list of supported options we can use with the StandardOpenOptions enum:

  • WRITE: opens the file for write access
  • APPEND: appends some data to the file
  • TRUNCATE_EXISTING: truncates the file
  • CREATE_NEW: creates a new file and throws an exception if the file already exists
  • CREATE: opens the file if it exists or creates a new file if it does not
  • DELETE_ON_CLOSE: deletes the file after closing the stream
  • SPARSE: the newly created file will be sparse
  • SYNC: preserves the content and the metadata of the file synchronized
  • DSYNC: preserves only the content of the file synchronized

In the next sections, we'll see examples of how to use each of these options.

To avoid any confusion on the file path, let's get a handle on the home directory of the user, which will be valid across all operating systems:

private static String HOME = System.getProperty("user.home");

3. Opening a File for Reading and Writing

First, if we want to create a new file if it does not exist we can use the option CREATE:

@Test
public void givenExistingPath_whenCreateNewFile_thenCorrect() throws IOException {
    assertFalse(Files.exists(Paths.get(HOME, "newfile.txt")));
    Files.write(path, DUMMY_TEXT.getBytes(), StandardOpenOption.CREATE);
    assertTrue(Files.exists(path));
}

We can also use the option CREATE_NEW, which will create a new file if it does not exist. However, it will throw an exception if the file already exists.

Secondly, if we want to open the file for reading we can use the newInputStream(Path, OpenOption...) method. This method opens the file for reading and returns an input stream:

@Test
public void givenExistingPath_whenReadExistingFile_thenCorrect() throws IOException {
    Path path = Paths.get(HOME, DUMMY_FILE_NAME);

    try (InputStream in = Files.newInputStream(path); BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
        String line;
        while ((line = reader.readLine()) != null) {
            assertThat(line, CoreMatchers.containsString(DUMMY_TEXT));
        }
    }
}

Notice how we didn't use the option READ because it's used by default by the method newInputStream.

Third, we can create a file, append to a file, or write to a file by using the newOutputStream(Path, OpenOption...) method. This method opens or creates a file for writing and returns an OutputStream.

The API will create a new file if we don't specify the open options, and the file does not exist. However, if the file exists, it will be truncated. This option is similar to calling the method with the CREATE and TRUNCATE_EXISTING options.

Let's open an existing file and append some data:

@Test
public void givenExistingPath_whenWriteToExistingFile_thenCorrect() throws IOException {
    Path path = Paths.get(HOME, DUMMY_FILE_NAME);

    try (OutputStream out = Files.newOutputStream(path, StandardOpenOption.APPEND, StandardOpenOption.WRITE)) {
        out.write(ANOTHER_DUMMY_TEXT.getBytes());
    }
}

4. Creating a SPARSE File

We can tell the file system that the newly created file should be sparse (files containing empty spaces that will not be written to disk).

For this, we should use the option SPARSE with the CREATE_NEW option. However, this option will be ignored if the file system does not support sparse files.

Let's create a sparse file:

@Test
public void givenExistingPath_whenCreateSparseFile_thenCorrect() throws IOException {
    Path path = Paths.get(HOME, "sparse.txt");
    Files.write(path, DUMMY_TEXT.getBytes(), StandardOpenOption.CREATE_NEW, StandardOpenOption.SPARSE);
}

5. Keeping the File Synchronized

The StandardOpenOptions enum has SYNC and DSYNC options. These options require that data is written to the file synchronously in the storage. In other words, these will guarantee that the data is not lost in the event of a system crash.

Let's append some data to our file and use the option SYNC:

@Test
public void givenExistingPath_whenWriteAndSync_thenCorrect() throws IOException {
    Path path = Paths.get(HOME, DUMMY_FILE_NAME);
    Files.write(path, ANOTHER_DUMMY_TEXT.getBytes(), StandardOpenOption.APPEND, StandardOpenOption.WRITE, StandardOpenOption.SYNC);
}

The difference between SYNC and DSYNC is that SYNC stores the content and the metadata of the file synchronously in the storage, while DSYNC stores only the contents of the file synchronously in the storage.

6. Deleting the File After Closing the Stream

The StandardOpenOptions enum also offers a useful option that gives us the ability to destroy the file after closing the stream. This useful if we want to create a temporary file.

Let's append some data to our file, and use the option DELETE_ON_CLOSE:

@Test
public void givenExistingPath_whenDeleteOnClose_thenCorrect() throws IOException {
    Path path = Paths.get(HOME, EXISTING_FILE_NAME);
    assertTrue(Files.exists(path)); // file was already created and exists

    try (OutputStream out = Files.newOutputStream(path, StandardOpenOption.APPEND, 
      StandardOpenOption.WRITE, StandardOpenOption.DELETE_ON_CLOSE)) {
        out.write(ANOTHER_DUMMY_TEXT.getBytes());
    }

    assertFalse(Files.exists(path)); // file is deleted
}

7. Conclusion

In this tutorial, we covered the available options to open files in Java using the new file system API (NIO2) that was shipped as a part of Java 7.

As usual, the source code with all the examples in the tutorial can be found over on Github.

Java bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
Comments are closed on this article!