Java I/O and NIO.2

NIO.2 cookbook, Part 1

A cookbook of recipes for performing common tasks in NIO.2

Java I/O and NIO.2

Show More

Oracle's Java 7 release introduced NIO.2, which extends its NIO (New I/O) framework with new capabilities (e.g., the ability to work with symbolic/soft links and hard links). This post launches a three-part series in which I present a cookbook of recipes for accomplishing various tasks with NIO.2.

Copying files

Q: How do I copy a file to another file?

A: You copy a file to another file by using the java.nio.file.Files class's public static Path copy(Path source, Path target, CopyOption... options) method, which copies a source file to a target file.

By default, the copy operation fails when the target file exists or is a symbolic link. However, when the source and target files are actually the same file, the method will complete without copying the file.

Some additional items to note:

  • File attributes will not necessarily be copied to the target file.
  • When symbolic links are supported, and the source file is a symbolic link, the final target of the link is copied.
  • When the source file is a directory, copy() creates an empty directory in the target location (directory entries are not copied).

Each java.nio.file.CopyOption argument passed to the options varargs array modifies copy()'s behavior. It's one of the following java.nio.file.StandardCopyOption and java.nio.file.LinkOption enum constants:

  • COPY_ATTRIBUTES: Attempt to copy the file attributes associated with this file to the target file. The exact file attributes that are copied is platform- and file system-dependent, and therefore unspecified. Minimally, last-modified-time is copied to the target file when supported by both the source and target file stores. Note that copying file timestamps may result in precision loss.
  • NOFOLLOW_LINKS: Symbolic links are not followed. When the file is a symbolic link, the symbolic link itself and not the target of the link is copied. It's implementation-specific as to whether file attributes can be copied to the new link. In other words, COPY_ATTRIBUTES may be ignored when copying a symbolic link.
  • REPLACE_EXISTING: When the target file exists, the target file is replaced unless it's a non-empty directory. When the target file exists and is a symbolic link, the symbolic link itself and not the target of the link is replaced.

copy() doesn't support StandardCopyOption's ATOMIC_MOVE option, which is meaningless in a file-copy context. I'll introduce ATOMIC_MOVE when I discuss file-moving later.

Along with java.lang.SecurityException, copy() throws one of the following exceptions:

  • java.nio.file.DirectoryNotEmptyException: REPLACE_EXISTING is specified but the file cannot be replaced because it's a non-empty directory
  • java.nio.file.FileAlreadyExistsException: the target file exists, but cannot be replaced because REPLACE_EXISTING isn't specified
  • IOException: an I/O error occurs
  • java.lang.UnsupportedOperationException: one of the CopyOptions in the varargs array passed to options is unsupported

I've created a small application that shows you how to use the most basic form of copy(). Listing 1 presents the application's source code.

Listing 1. Copy.java

import java.io.IOException;

import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.Files;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Copy
{
   public static void main(String[] args)
   {
      if (args.length != 2)
      {
         System.err.println("usage: java Copy source target");
         return;
      }

      Path source = Paths.get(args[0]);
      Path target = Paths.get(args[1]);

      try 
      {
         Files.copy(source, target);
      } 
      catch (FileAlreadyExistsException faee) 
      {
         System.err.printf("%s: file already exists%n", target);
      } 
      catch (DirectoryNotEmptyException dnee) 
      {
         System.err.printf("%s: not empty%n", target);
      } 
      catch (IOException ioe)
      {
         System.err.printf("I/O error: %s%n", ioe.getMessage());
      }
   }
}

Listing 1's main() method first validates the command line to ensure that two arguments, representing the source and target files, have been specified. If not, usage information is output and the program ends.

Next, the java.nio.file.Paths class's public static Path get(URI uri) method is called twice to obtain java.nio.file.Path objects representing the named source and target files in the file system.

The Path objects are now passed to copy(). If this method succeeds, nothing is output. Otherwise, a suitable error message is reported.

Compile Listing 1 (javac Copy.java) and play with the application. For example, execute java Copy Copy.java Copy.bak. You might want to try copying a non-empty directory to another directory. What happens?

As an exercise, modify Copy.java to process additional command-line arguments that identify CopyOptions. Pass the equivalent enum constants to copy() and explore their impacts on copy()'s behavior.

Later in this series, I'll present a recipe that expands Listing 1 so that Copy can copy an entire directory hierarchy of files and subdirectories to another directory.

Deleting files and directories

Q: How do I delete a file or directory?

A: You delete a file or directory by using the Files class's public static void delete(Path path) method, which deletes the file or directory identified by path:

  • If path refers to an open file that's in use, some operating systems may prevent it from being removed.
  • If path refers to a directory, the directory must be empty (apart from any operating system-specific special files).
  • If path refers to a symbolic link, this method deletes the symbolic link and not the link's target.

delete() throws IOException when an I/O error occurs, java.nio.file.NoSuchFileException when the file doesn't exist, and DirectoryNotEmptyException when the directory isn't empty.

I've created a small application that shows you how to use delete(). Listing 2 presents the application's source code.

Listing 2. Delete.java

import java.io.IOException;

import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Delete
{
   public static void main(String[] args)
   {
      if (args.length != 1)
      {
         System.err.println("usage: java Delete file-or-directory");
         return;
      }

      Path path = Paths.get(args[0]);
      try 
      {
         Files.delete(path);
      } 
      catch (NoSuchFileException nsfe) 
      {
         System.err.printf("%s: no such file or directory%n", path);
      } 
      catch (DirectoryNotEmptyException dnee) 
      {
         System.err.printf("%s: not empty%n", path);
      } 
      catch (IOException ioe)
      {
         System.err.printf("I/O error: %s%n", ioe.getMessage());
      }
   }
}

Listing 2's main() method first validates the command line to ensure that a single argument, which is considered to be a file or directory path, has been specified. If not, usage information is output and the program ends.

Next, the Paths class's get() method is called to obtain a Path object representing the named file or directory in the file system.

The Path object is now passed to delete(). If this method succeeds, nothing is output. Otherwise, a suitable error message is reported.

Compile Listing 2 (javac Delete.java) and play with the application. Try deleting a read/write file, a read-only file, a non-empty directory, and a symbolic link; and observe what happens.

Moving files

Q: How do I move a file to another file?

A: You move a file to another file by using the Files class's public static Path move(Path source, Path target, CopyOption... options) method, which moves a source file to a target file.

By default, this method attempts to move the source file to the target file, failing when the target file exists except when the source and target are the same file, in which case this method has no effect.

Each CopyOption argument passed to the options varargs array modifies move()'s behavior. It's one of the following StandardCopyOption enum constants:

  • ATOMIC_MOVE: The move is performed as an atomic file system operation and all other options are ignored. When the target file exists, it's implementation-specific as to whether the existing file is replaced or this method fails by throwing IOException. If the move cannot be performed as an atomic file system operation, java.nio.file.AtomicMoveNotSupportedException is thrown.
  • REPLACE_EXISTING: When the target file exists, the target file is replaced unless it's a non-empty directory. When the target file exists and is a symbolic link, the symbolic link itself and not the target of the link is replaced.

Apart from AtomicMoveNotSupportedException, move() throws the same exceptions as thrown from copy().

I've created a small application that shows you how to use the most basic form of move(). Listing 3 presents the application's source code, which is nearly identical to Listing 1.

Listing 3. Move.java

import java.io.IOException;

import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.Files;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Move
{
   public static void main(String[] args)
   {
      if (args.length != 2)
      {
         System.err.println("usage: java Move source target");
         return;
      }

      Path source = Paths.get(args[0]);
      Path target = Paths.get(args[1]);

      try 
      {
         Files.move(source, target);
      } 
      catch (FileAlreadyExistsException faee) 
      {
         System.err.printf("%s: file already exists%n", target);
      } 
      catch (DirectoryNotEmptyException dnee) 
      {
         System.err.printf("%s: not empty%n", target);
      } 
      catch (IOException ioe)
      {
         System.err.printf("I/O error: %s%n", ioe.getMessage());
      }
   }
}

Compile Listing 3 (javac Move.java) and play with the application. For example, assuming a file named report.txt, execute java Move report.txt report.bak. What happens when you move a file to a file that already exists?

As an exercise, modify Move.java to process additional command-line arguments that identify CopyOptions. Pass the equivalent enum constants to move() and explore their impacts on move()'s behavior.

What's next?

In Part 2, I present path-related recipes (such as obtaining paths and retrieving path information), file/directory-testing recipes (such as testing file/directory existence), and attribute-oriented recipes.

download
Get the source code for this post's applications. Created by Jeff Friesen for JavaWorld

The following software was used to develop the post's code:

  • 64-bit JDK 7u6

The post's code was tested on the following platform(s):

  • JVM on 64-bit Windows 7 SP1
Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.