Java 7: Managing Files and Directories

0
209
12 min read

(For more resources on Java, see here.)

Introduction

It is often necessary to perform file manipulations, such as creating files, manipulating their attributes and contents, or removing them from the filesystem. The addition of the java.lang.object.Files class in Java 7 simplifies this process. This class relies heavily on the use of the new java.nio.file.Path interface. The methods of the class are all static in nature, and generally assign the actual file manipulation operations to the underlying filesystem.

Many of the operations described in this chapter are atomic in nature, such as those used to create and delete files or directories. Atomic operations will either execute successfully to completion or fail and result in an effective cancellation of the operation. During execution, they are not interrupted from the standpoint of a filesystem. Other concurrent file operations will not impact the operation.

To execute many of the examples in this chapter, the application needs to run as administrator. To run an application as administrator under Windows, right-click on the Command Prompt menu and choose Run as administrator. Then navigate to the appropriate directory and execute using the java.exe command. To run as administrator on a UNIX system, use the sudo command in a terminal window followed by the java command.

Basic file management is covered in this chapter. The methods required for the creation of files and directories are covered in the Creating Files and Directories recipe. This recipe focuses on normal files. The creation of temporary files and directories is covered in the Managing temporary files and directories recipe and the creation of linked files is covered in the Managing symbolic links recipe.

The options available for copying files and directories are found in the Controlling how a file is copied recipe. The techniques illustrated there provide a powerful way of dealing with file replication. Moving and deleting files and directories are covered in the Moving a file or directory and Deleting files and directories recipes, respectively.

The Setting time-related attributes of a file or directory recipe illustrates how to assign time attributes to a file. Related to this effort are other attributes, such as file ownership and permissions. File ownership is addressed in the Managing file ownership recipe. File permissions are discussed in two recipes: Managing ACL file permissions and Managing POSIX file permissions.

Creating files and directories

The process of creating new files and directories is greatly simplified in Java 7. The methods implemented by the Files class are relatively intuitive and easy to incorporate into your code. In this recipe, we will cover how to create new files and directories using the createFile and createDirectory methods.

Getting ready

In our example, we are going to use several different methods to create a Path object that represents a file or directory. We will do the following:

  1. Create a Path object.
  2. Create a directory using the Files class’ createDirectory method.
  3. Create a file using the Files class’ createFile method.

The FileSystem class’ getPath method can be used to create a Path object, as can the Paths class’ get method. The Paths class’ static get method returns an instance of a Path based on a string sequence or a URI object. The FileSystem class’ getPath method also returns a Path object, but only uses a string sequence to identify the file.

How to do it…

  1. Create a console application with a main method. In the main method, add the following code that creates a Path object for the directory /home/test in the C directory. Within a try block, invoke the createDirectory method with your Path object as the parameter. This method will throw an IOException if the path is invalid. Next, create a Path object for the file newFile.txt using the createFile method on this Path object, again catching the IOException as follows:
  2. try{

    Path testDirectoryPath = Paths.get("C:/home/test");
    Path testDirectory = Files.createDirectory(testDirectoryPath);
    System.out.println("Directory created successfully!");
    Path newFilePath = FileSystems.getDefault().

    getPath("C:/home/test/newFile.txt");
    Path testFile = Files.createFile(newFilePath);
    System.out.println("File created successfully!");
    }
    catch (IOException ex){

    ex.printStackTrace();
    }

  3. Execute the program. Your output should appear as follows:
    Directory created successfully!
    File created successfully!
  4. Verify that the new file and directory exists in your filesystem. Next, add a catch block prior to the IOException after both methods, and catch a FileAlreadyExistsException:
  5. }
    catch (FileAlreadyExistsException a){

    System.out.println("File or directory already exists!");
    }
    catch (IOException ex){

    ex.printStackTrace();
    }

  6. When you execute the program again, your output should appear as follows:
    File or directory already exists!

How it works…

The first Path object was created and then used by the createDirectory method to create a new directory. After the second Path object was created, the createFile method was used to create a file within the directory, which had just been created. It is important to note that the Path object used in the file creation could not be instantiated before the directory was created, because it would have referenced an invalid path. This would have resulted in an IOException.

When the createDirectory method is invoked, the system is directed to check for the existence of the directory first, and if it does not exist, create it. The createFile method works in a similar fashion. The method fails if the file already exists. We saw this when we caught the FileAlreadyExistsException. Had we not caught that exception, an IOException would have been thrown. Either way, the existing file would not be overwritten.

There’s more…

The createFile and createDirectory methods are atomic in nature. The createDirectories method is available to create directories, as discussed next. All three methods provide the option to pass file attribute parameters for more specific file creation.

Using the createDirectories method to create a hierarchy of directories

The createDirectories method is used to create a directory and potentially other intermediate directories. In this example, we build upon the previous directory structure by adding a subtest and a subsubtest directory to the test directory. Comment out the previous code that created the directory and file and add the following code sequence:

Path directoriesPath = Paths.

get("C:/home/test/subtest/subsubtest");
Path testDirectory = Files.createDirectories(directoriesPath);

Verify that the operation succeeded by examining the resulting directory structure.

See also

Creating temporary files and directories is covered in the Managing temporary files and directories recipe. The creation of symbolic files is illustrated in the Managing symbolic links recipe.

Controlling how a file is copied

The process of copying files is also simplified in Java 7, and allows for control over the manner in which they are copied. The Files class’ copy method supports this operation and is overloaded providing three techniques for copying those which differ by their source or destination.

Getting ready

In our example, we are going to create a new file and then copy it to another target file. This process involves:

  1. Creating a new file using the createFile method.
  2. Creating a path for the destination file.
  3. Copying the file using the copy method.

How to do it…

  1. Create a console application with a main method. In the main method, add the following code sequence to create a new file. Specify two Path objects, one for your initial file and one for the location where it will be copied. Then add the copy method to copy that file to the destination location as follows:

  2. Path newFile = FileSystems.getDefault().

    getPath("C:/home/docs/newFile.txt");
    Path copiedFile = FileSystems.getDefault().

    getPath("C:/home/docs/copiedFile.txt");
    try{

    Files.createFile(newFile);
    System.out.println("File created successfully!");
    Files.copy(newFile, copiedFile);
    System.out.println("File copied successfully!");
    }
    catch (IOException e){

    System.out.println("IO Exception.");
    }

  3. Execute the program. Your output should appear as follows:
    File created successfully!
    File copied successfully!
  4. When you execute the program again, your output should appear as follows:
    File copied successfully!

How it works…

The createFile method created your initial file, and the copy method copied that file to the location specified by the copiedFile variable. If you were to attempt to run that code sequence twice in a row, you would have encountered an IOException, because the copy method will not, by default, replace an existing file. The copy method is overloaded. The second form of the copy method used the java.lang.enum.StandardCopyOption enumeration value of REPLACE_EXISTING, which allowed the file to be replaced.

The three enumeration values for StandardCopyOption are listed in the following table:

Value

Meaning

ATOMIC_MOVE

Perform the copy operation

atomically

COPY_ATTRIBUTES

Copy the source file attributes

to the destination file

REPLACE_EXISTING

Replace the existing file

if it already exists

Replace the copy method call in the previous example with the following:

Files.copy(newFile, copiedFile, StandardCopyOption.REPLACE_EXISTING);

When the code executes, the file should be replaced. Another example of the use of the copy options is found in the There’s more… section of the Moving a file and directory recipe.

There’s more…

If the source file and the destination file are the same, then the method completes, but no copy actually occurs. The copy method is not atomic in nature.

There are two other overloaded copy methods. One copies a java.io.InputStream to a file and the other copies a file to a java.io.OutputStream. In this section, we will examine, in more depth, the processes of:

  • Copying a symbolic link file
  • Copying a directory
  • Copying an input stream to a file
  • Copying a file to an output stream

Copying a symbolic link file

When a symbolic link file is copied, the target of the symbolic link is copied. To illustrate this, create a symbolic link file called users.txt in the music directory to the users.txt file in the docs directory.

Use the following code sequence to perform the copy operation:


Path originalLinkedFile = FileSystems.getDefault(). getPath("C:/home/music/users.txt");
Path newLinkedFile = FileSystems.getDefault(). getPath("C:/home/music/users2.txt");
try{

Files.copy(originalLinkedFile, newLinkedFile);
System.out.println("Symbolic link file copied successfully!");
}
catch (IOException e){

System.out.println("IO Exception.");
}

Execute the code. You should get the following output:
Symbolic link file copied successfully!

Examine the resulting music directory structure. The user2.txt file has been added and is not connected to either the linked file or the original target file. Modification of the user2.txt does not affect the contents of the other two files.

Copying a directory

When a directory is copied, an empty directory is created. The files in the original directory are not copied. The following code sequence illustrates this process:

Path originalDirectory = FileSystems.getDefault(). getPath("C:/home/docs");
Path newDirectory = FileSystems.getDefault(). getPath("C:/home/tmp");
try{

Files.copy(originalDirectory, newDirectory);
System.out.println("Directory copied successfully!");
}
catch (IOException e){

e.printStackTrace();
}

When this sequence is executed, you should get the following output:
Directory copied successfully!

Examine the tmp directory. It should be empty as any files in the source directory are not copied.

Copying an input stream to a file

The copy method has a convenient overloaded version that permits the creation of a new file based on the input from an InputStream. The first argument of this method differs from the original copy method, in that it is an instance of an InputStream.

The following example uses this method to copy the jdk7.java.net website to a file:

Path newFile = FileSystems.getDefault(). getPath("C:/home/docs/java7WebSite.html");
URI url = URI.create("http://jdk7.java.net/");
try (InputStream inputStream = url.toURL().openStream())

Files.copy(inputStream, newFile);
System.out.println("Site copied successfully!");
}
catch (MalformedURLException ex){

ex.printStackTrace();
}
catch (IOException ex){

ex.printStackTrace();
}

When the code executes, you should get the following output:
Site copied successfully!

A java.lang.Object.URI object was created to represent the website. Using the URI object instead of a java.lang.Object.URL object immediately avoids having to create a separate try-catch block to handle the MalformedURLException exception.

The URL class’ openStream method returns an InputStream, which is used as the first parameter of the copy method.

The copy method was then executed. The new file can now be opened with a browser or otherwise can be processed as needed. Notice that the method returns a long value representing the number of bytes written.

Copying a file to an output stream

The third overloaded version of the copy method will open a file and write its contents to an OutputStream. This can be useful when the content of a file needs to be copied to a non-file object such as a PipedOutputStream. It can also be useful when communicating to other threads or writing to an array of bytes, as illustrated here. In this example, the content of the users.txt file is copied to an instance of a ByteArrayOutputStream>. Its toByteArray method is then used to populate an array as follows:


Path sourceFile = FileSystems.getDefault(). getPath("C:/home/docs/users.txt");
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {

Files.copy(sourceFile, outputStream);
byte arr[] = outputStream.toByteArray();
System.out.println("The contents of " + sourceFile.getFileName());
for(byte data : arr) {

System.out.print((char)data);
}
System.out.println();
}
catch (IOException ex) {

ex.printStackTrace();
}

Execute this sequence. The output will depend on the contents of your file, but should be similar to the following:

The contents of users.txt
Bob
Jennifer
Sally
Tom
Ted

Notice the use of the try-with-resources block that handles the opening and closing of the file. It is always a good idea to close the OutputStream, when the copy operation is complete or exceptions occur. The try-with-resources block handles this nicely. The method may block until the operation is complete in certain situations. Much of its behavior is implementation-specific. Also, the output stream may need to be flushed since it implements the Flushable interface. Notice that the method returns a long value representing the number of bytes written.

See also

See the Managing symbolic links recipe for more details on working with symbolic links.

LEAVE A REPLY

Please enter your comment!
Please enter your name here