Main Page

Previous Next

Creating File Output Streams

You use a FileOutputStream object when you want to write to a physical file on a disk. The FileOutputStream class is derived from the OutputStream class and therefore inherits the methods of that class for writing to a file. However, we won't bother going into detail on these, or the versions in the FileOutputStream class that override them, as we will be using the new file channel capability to write to a file.

There are five constructors for FileOutputStream objects:

Constructor

Description

FileOutputStream
(String filename)

Creates an output stream for the file filename. The existing contents of the file will be overwritten. If the file cannot be opened for any reason, an exception of type FileNotFoundException will be thrown.

FileOutputStream
(String filename, boolean append)

Creates an output stream for the file filename. Data written to the file will be appended following the existing contents if append is true. If append is false any existing file contents will be overwritten. If the file cannot be opened for any reason, an exception of type FileNotFoundException will be thrown.

FileOutputStream
(File file)

Creates a file output stream for the file represented by the object file. Any existing file contents will be overwritten. If the file cannot be opened, an exception of type FileNotFoundException will be thrown.

FileOutputStream
(File file, boolean append)

Creates a file output stream for the file represented by the object file. Data written to the file will be appended following the existing contents if append is true. If append is false any existing file contents will be overwritten. If the file cannot be opened for writing for any reason, an exception of type FileNotFoundException will the thrown.

FileOutputStream
(FileDescriptor desc)

Creates an output stream corresponding to the argument desc. A FileDescriptor object represents an existing connection to a file so, since the file must exist, this constructor does not throw an exception of type FileNotFoundException.

The first four constructors will also create the file if it does not already exist, but only if the parent directory exists, so it is a good idea to check this. All of these constructors can throw a SecurityException if writing to the file is not authorized on your system, although by default there is no security manager for an application in which case there are no restrictions on writing files. Once you have created a FileOutputStream object, the physical file is automatically opened, ready to be written. Once you have written the file, using a channel as we shall see in Chapter 10, you can close the file by calling the close() method for the FileOutputStream object. This also closes the file channel and releases all system resources associated with the stream.

To create a stream object of type FileOutputStream, you will typically create a File object first, and then pass that to a FileOutputStream constructor. This approach enables you to check the properties of the file using the File object before you try to create the stream and thus, avoid potential problems. Of course, you can create a FileOutputStream object directly from a String object that defines the path and file name, but it is generally much less convenient to do this. We will come back to the third possibility – creating a file stream object from a FileDescriptor object – in a moment.

In passing, here is how you would create a file output stream directly from the file name:

FileOutputStream outputFile = null;   // Place to store the stream reference
try {
  outputFile = new FileOutputStream("myFile.txt");
} catch(FileNotFoundException e) {
  e.printStackTrace(System.err);
}

If the file cannot be opened, the constructor will throw a FileNotFoundException, which won't be very convenient in most circumstances. We must put the call to the constructor in a try block and catch the exception if we want the code to compile, unless of course we arrange for the method containing the constructor call to pass on the exception with a throws clause. The exception will be thrown if the path refers to a directory rather than a file, or if the parent directory in the path does not exist. If the file does not exist, but the directory that is supposed to contain it does exist, the constructor will create a new file for you. Creating a File object first enables you to check the file out and deal with any potential problems. Let's look at ways in which you can apply this.

Ensuring a File Exists

Let's suppose that you want to append data to a file if it exists, and create a new file if it doesn't. Either way, you want to end up with a file output stream to work with. You will need to go through several checks to achieve this:

  • Use the File object to verify that it actually represents a file rather than a directory. If it doesn't, you can't go any further so output an error message.

  • Use the File object to decide whether the file exists. If it doesn't, ensure that you have a File object with an absolute path. You need this to obtain and check out the parent directory.

  • Get the path for the parent directory and create another File object using this path. Use the new File object to check whether the parent directory exists. If it doesn't, create it using the mkDirs() method for the new File object.

Let's look at how that might be done in practice.

Try It Out – Ensure that a File Exists

You could guarantee a file is available with the following code:

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;

public class GuaranteeAFile {
  public static void main(String[] args) {
    String filename = "C:/Beg Java Stuff/Bonzo/Beanbag/myFile.txt";
    File aFile = new File(filename);    // Create the File object

    // Verify the path is a file
    if (aFile.isDirectory()) {

      // Abort after a message
      // You could get input from the keyboard here and try again...
      System.out.println("The path " + aFile.getPath()
                         + " does not specify a file. Program aborted.");
      System.exit(1);
    }
    // If the file doesn't exist
    if (!aFile.isFile()) {
      // Check the parent directory...
      aFile = aFile.getAbsoluteFile();
      File parentDir = new File(aFile.getParent());
      if (!parentDir.exists()) {   // ... and create it if necessary
        parentDir.mkdirs();
      } 
    } 

    // Place to store the stream reference
    FileOutputStream outputFile = null; 
    try {

      // Create the stream opened to append data
      outputFile = new FileOutputStream(aFile, true);
    } catch (FileNotFoundException e) {
      e.printStackTrace(System.err);
    } 
    System.exit(0);
  } 
}

After executing this code, you should find that the directories and the file have been created. You can try this out with paths with a variety of directory levels. Delete them all when you have done, though.

How It Works

We call isDirectory() in the if statement to see whether the path is just a directory. Instead of aborting at this point we could invite input of a new path from the keyboard, but I'll leave that for you to try. Next we check whether the file exists. If it doesn't we call getAbsoluteFile() to ensure that our File object has a parent path. If we don't do this and we have a file specified with a parent, in the current directory for instance, then getParent() will return null. Having established the File object with an absolute path, we create a File object for the directory containing the file. If this directory does not exist, calling mkdirs() will create all the directories required for the path so that we can then safely create the file stream. The FileOutputStream constructor can in theory throw a FileNotFoundException although not in our situation here. In any event, we must put the try and catch block in for the exception.

A further possibility is that you might start with two strings defining the directory path and the file name separately. You might then want to be sure that you had a valid directory before you created the file. You could do that like this:

String dirname = "C:/Beg Java Stuff";   // Directory name
String filename = "charData.txt";       // File name

File dir = new File(dirname);           // File object for directory
if (!dir.exists()) {     // If directory does not exist
  if (!dir.mkdirs()) {   // ...create it
    System.out.println("Cannot create directory: " + dirname);
    System.exit(1);
  } 
} else if (!dir.isDirectory()) {
  System.err.println(dirname + " is not a directory");
  System.exit(1);
}

// Now create the file...

If the directory doesn't exist, we call mkdirs() inside the nested if to create it. Since the method returns false if the directory was not created, this will check whether or not we have indeed managed to create the directory.

Avoiding Overwriting a File

In some situations, when the file does exist you may not want it to be overwritten. Here is one way you could avoid overwriting a file if it already exists:

String filename = "C:/Beg Java Stuff/myFile.txt";
File aFile = new File(filename);
FileOutputStream outputFile = null;   // Place to store the stream reference
if (aFile.isFile()) {
  System.out.println("myFile.txt already exists.");
} else {
  // Create the file stream
  try {
    // Create the stream opened to append data
    outputFile = new FileOutputStream(aFile); 
    System.out.println("myFile.txt output stream created");
  } catch (FileNotFoundException e) {
    e.printStackTrace(System.err);
  }
}

Of course, if you want to be sure that the path will in fact result in a new file being created when it doesn't already exist, you would need to put in the code from the previous example that checks out the parent directory. The fragment above avoids overwriting the file but it is not terribly helpful. If the file exists, we create the same FileOutputStream object as before, but if it doesn't, we just toss out an error message. In practice, you are more likely to want the program to take some action so that the existing file is protected but the new file still gets written. One solution would be to rename the original file in some way if it exists, and then create the new one with the same name as the original. This takes a little work though.

Try It Out – Avoid Overwriting a File

Without worrying about plugging in the code that ensures that the file directory exists, here is how we could prevent an existing file from being overwritten.

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;

public class AvoidOverwritingFile {
  public static void main(String[] args) {
    String filepath = "C:/Beg Java Stuff/myFile.txt";
    File aFile = new File(filepath);
    FileOutputStream outputFile = null;     // Stores the stream reference
    if (aFile.isFile()) {
      File newFile = aFile;                 // Start with the original file

      // We will append "_old" to the file
      // name repeatedly until it is unique
      do {
        String name = newFile.getName();    // Get the name of the file
        int period = 
          name.indexOf('.');         // Find the separator for the extension
        newFile = new File(newFile.getParent(), 
                           name.substring(0, period) + "_old" 
                           + name.substring(period));
      } while (!aFile.renameTo(newFile));   // Stop when renaming works
    } 

    // Now we can create the new file
    try {

      // Create the stream opened to append data
      outputFile = new FileOutputStream(aFile);
      System.out.println("myFile.txt output stream created");
    } catch (FileNotFoundException e) {
      e.printStackTrace(System.err);
    } 
    System.exit(0);
  }
}

If you run this a few times, you should see some _old_old... files created.

How It Works

If the file exists, we execute the code in the if block. This stores the reference to the original File object in newFile as a starting point for the do-while loop that follows. Each iteration of the loop will append the string _old to the name of the file and create a new File object using this name in the original directory. The expression in the loop condition then tries to rename the original file using the new File object. If this fails, we go round the loop again and add a further occurrence of _old to the file name. Eventually we should succeed as long as we don't do it so often that we exceed the permitted file name length of the system. The getParent() method gives us the parent directory for the file and the getName() method returns the file name. We have to split the file name into the name part and the extension to append our _old string, and the charAt() method for the String object gives us the index position of the separating period. Of course, this code presumes the existence of a file extension since we define our original file name with one. It is quite possible to deal with files that don't have an extension but I'll leave that as a little digression for you.

FileDescriptor Objects

A FileOutputStream object has a method, getFD(), which returns an object of type FileDescriptor that represents the current connection to the physical file. You cannot create a FileDescriptor object yourself. You can only obtain a FileDescriptor object by calling the getFD() method for an object that represents a file stream. Once you have closed the stream, you can no longer obtain the FileDescriptor object for it since the connection to the file will have been terminated.

You can use a FileDescriptor object to create other stream objects when you want to have several connected to the same file concurrently. Since a FileDescriptor object represents an existing connection, you can only use it to create streams with read and/or write permissions that are consistent with the original stream. You can't use the FileDescriptor object from a FileOutputStream to create a FileInputStream for instance.

If you look at the documentation for the FileDescriptor class, you'll see that it also defines three public static data members: in, out, and err, which are themselves of type FileDescriptor. These correspond to the standard system input, the standard system output, and the standard error stream respectively, and they are there as a convenience for when you want to create byte or character stream objects corresponding to the standard streams.

Important 

Don't confuse the data members of the FileDescriptor class with the data members of the same name defined by the System class in the java.lang package. The in, out, and err data members of the System class are of type PrintStream, so they have the print() and println() methods. The FileDescriptor data members do not.

Previous Next
JavaScript Editor Java Tutorials Free JavaScript Editor