«1. Обзор

В этом руководстве мы сосредоточимся на стандартных параметрах открытия, доступных для файлов в Java.

Мы рассмотрим перечисление StandardOpenOption, реализующее интерфейс OpenOption и определяющее эти стандартные параметры открытия.

2. Параметр OpenOption

В Java мы можем работать с файлами, используя NIO2 API, который содержит несколько служебных методов. Некоторые из этих методов используют необязательный параметр OpenOption, который настраивает способ открытия или создания файла. Кроме того, если этот параметр не задан, он будет иметь значение по умолчанию, которое может быть разным для каждого из этих методов.

Тип перечисления StandardOpenOption определяет стандартные параметры и реализует интерфейс OpenOption.

Вот список поддерживаемых опций, которые мы можем использовать с перечислением StandardOpenOptions:

    WRITE: открывает файл для доступа для записи APPEND: добавляет некоторые данные в файл TRUNCATE_EXISTING: усекает файл CREATE_NEW: создает новый файл и генерирует исключение, если файл уже существует CREATE: открывает файл, если он существует, или создает новый файл, если он не существует DELETE_ON_CLOSE: удаляет файл после закрытия потока SPARSE: вновь созданный файл будет разреженным SYNC: сохраняет содержимое и метаданные синхронизированный файл DSYNC: сохраняет только содержимое синхронизированного файла

В следующих разделах мы увидим примеры использования каждой из этих опций.

Чтобы избежать путаницы в пути к файлу, давайте получим дескриптор домашнего каталога пользователя, который будет действителен во всех операционных системах:

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

3. Открытие файла для чтения и записи ~~ ~ Во-первых, если мы хотим создать новый файл, если он не существует, мы можем использовать опцию CREATE:

Мы также можем использовать опцию CREATE_NEW, которая создаст новый файл, если он не существует. Однако он выдаст исключение, если файл уже существует.

@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));
}

Во-вторых, если мы хотим открыть файл для чтения, мы можем использовать метод newInputStream(Path, OpenOption…). Этот метод открывает файл для чтения и возвращает входной поток:

Обратите внимание, что мы не использовали опцию READ, потому что она используется по умолчанию методом newInputStream.

@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));
        }
    }
}

В-третьих, мы можем создать файл, добавить в файл или записать в файл с помощью метода newOutputStream(Path, OpenOption…). Этот метод открывает или создает файл для записи и возвращает OutputStream.

API создаст новый файл, если мы не укажем параметры открытия, а файл не существует. Однако, если файл существует, он будет усечен. Эта опция аналогична вызову метода с опциями CREATE и TRUNCATE_EXISTING.

Давайте откроем существующий файл и добавим некоторые данные:

4. Создание разреженного файла

@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());
    }
}

Мы можем указать файловой системе, что вновь созданный файл должен быть разреженным (файлы, содержащие пустые места, которые не будут быть записаны на диск).

Для этого мы должны использовать опцию SPARSE с опцией CREATE_NEW. Однако этот параметр будет проигнорирован, если файловая система не поддерживает разреженные файлы.

Давайте создадим разреженный файл:

5. Синхронизация файла

@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);
}

В перечислении StandardOpenOptions есть опции SYNC и DSYNC. Эти параметры требуют, чтобы данные записывались в файл синхронно в хранилище. Другими словами, это гарантирует, что данные не будут потеряны в случае сбоя системы.

Добавим некоторые данные в наш файл и воспользуемся опцией SYNC:

Разница между SYNC и DSYNC в том, что SYNC синхронно сохраняет содержимое и метаданные файла в хранилище, а DSYNC хранит только содержимое файла синхронно в хранилище.

@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);
}

6. Удаление файла после закрытия потока

Перечисление StandardOpenOptions также предлагает полезную опцию, которая дает нам возможность уничтожить файл после закрытия потока. Это полезно, если мы хотим создать временный файл.

Добавим некоторые данные в наш файл и воспользуемся опцией DELETE_ON_CLOSE:

7. Заключение

@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
}

«В этом руководстве мы рассмотрели доступные варианты открытия файлов в Java с помощью нового API файловой системы (NIO2), который был включен в состав Java 7.

Как обычно, исходный код со всеми примерами в руководстве может можно найти на Github.

«