Copying an Entire Directory – Java I/O: Part II

Copying an Entire Directory

The Files.copy() method only copies a single file or creates an empty directory at its destination, depending on whether the source is a file or a directory, respectively. Shown below is the copyEntireDirectory() method that copies an entire directory.

Click here to view code image

/** Declared in FileUtils class.
 * Copy an entire directory.
 * @param sourceDir        Directory to copy.
 * @param destinationDir   Directory to which the source directory is copied.
 * @param options          Copy options for all entries.
 */
public static void copyEntireDirectory(Path sourceDir,
                                       Path destinationDir,
                                       CopyOption… options)  {
  try (Stream<Path> stream = Files.walk(sourceDir)) {                    // (1)
    stream.forEach(entry -> {
      Path relativeEntryPath = sourceDir.relativize(entry);              // (2)
      Path destination  = destinationDir.resolve(relativeEntryPath);     // (3)
      try {
        Files.copy(entry, destination, options);                         // (4)
      } catch (DirectoryNotEmptyException e) {
        e.printStackTrace();
      } catch (IOException e) {
        e.printStackTrace();
      }
    });
  } catch (IOException e) {
    e.printStackTrace();
  }
}

The method copyEntireDirectory() copies each entry with the Files.copy() method at (4) as the directory hierarchy is traversed in the stream created by the Files.walk() method at (1). For each entry, the destination where the entry should be copied is determined by the code at (2) and (3).

First, the relative path between the source directory path and the current entry path is determined by the relativize() method at (2):

Click here to view code image

Source directory:      ./a/b
Current entry:         ./a/b/c/d
Relative entry path:   c/d

The destination path to copy the current entry is determined by joining the destination directory path with the relative entry path by calling the resolve() method at (3):

Click here to view code image

Destination directory:   ./x/y
Relative entry path:     c/d
Destination entry paths: ./x/y/c/d

In the scenario above, the entry ./a/b/c/d is copied to the destination path ./x/y/c/ d during the copying of source directory ./a/b to the destination directory ./x/y.

Click here to view code image

CopyOption[] options = new CopyOption[] {
    StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES,
    LinkOption.NOFOLLOW_LINKS};
Path sourceDirectory      = Path.of(“.”, “a”, “b”);       //      ./a/b
Path destinationDirectory = Path.of(“.”, “x”, “y”);       // (5a) ./x/y
// Path destinationDirectory = Path.of(“.”, “x”)
//              .resolve(sourceDirectory.getFileName());  // (5b) ./x/b
FileUtils.copyEntireDirectory(sourceDirectory, destinationDirectory, options);

If the destination directory should have the same name as the source directory, we can use the code at (5b) rather than the code at (5a).

A few things should be noted about the declaration of the copyEntireDirectory() method. It can be customized to copy the entries by specifying copy options. It closes the stream created by the walk() method in a try-with-resources statement. Calling the method copy() in the body of the lambda expression requires handling any IOException inside the lambda body. Any IOException from calling the walk() method is also explicitly handled.

Leave a Reply

Your email address will not be published. Required fields are marked *