Working With Directories

File and Stream Guide: [ nsIScriptableIO | Accessing Files | Getting File Information | Reading from Files | Writing to Files | Moving, Copying and Deleting Files | Uploading and Downloading Files | Working With Directories ]

Important note: The pages from the File and Stream Guide use the IO object (nsIScriptableIO), which was not available in any released version of the platform (pending some fixes). There are alternative XPCOM APIs you can use, your help in updating this pages to use the supported API is very much welcome!

Other documentation on files and I/O not using the unavailable nsIScriptableIO APIs: Code snippets: File I/O, Open and Save Dialogs, Reading textual data, Writing textual data, List of file-related error codes.

A reference to a directory may be created in the same way as with a file by using nsIScriptableIO.getFile(). You can access a subdirectory by supplying a directory name as the second argument, or refer to one of the special directories by supplying a null string for the second argument. The first line below with retrieve the directory corresponding to the user's home directory, while the second will retrieve the directory 'myfiles' on the desktop.

var dir1 = IO.getFile("Home", "");
var dir2 = IO.getFile("Desktop", "myfiles");

To refer to a subdirectory, rather than using a path, use nsIFile.append() to build up a path. For instance:

var dir2 = IO.getFile("Desktop", "myfiles");
dir2.append("pictures");
dir2.append("vacations");

In this example, a subdirectory several levels down is referenced. Each call to nsIFile.append() navigates to a further subdirectory. The result is a subdirectory three levels below the desktop directory. These directories do not need to exist yet, but they can be created using nsIFile.create(). See Creating Directories below for information about this. Note that nsIFile.append() modifies the file object, rather than returning a new one.

Both files and directories are represented using the same kind of object so most of the functions available for nsIFile will work for both. You can check if the object refers to a directory or a file by using nsIFile.isDirectory(). This method returns true if a file object returned by nsIScriptableIO.getFile() refers to a directory, and false otherwise. There is also a corresponding nsIFile.isFile() method to check if an object refers to a file. In both cases, the method fails if the file or directory does not exist, so you should check first using nsIFile.exists().

var dir = IO.getFile("Desktop", "myfiles");
dir.append("pictures");
if (dir.exists() && dir.isDirectory())
  alert("This is a directory");

Creating Directories

You can create a new directory by using nsIFile.create(). For directories, the first argument to this method should be the constant DIRECTORY_TYPE (which has a value of 1). To create a file instead, you could use the constant NORMAL_FILE_TYPE instead.

var dir = IO.getFile("Desktop", "myfiles");
dir.append("pictures");
if (!dir.exists())
  dir.create(dir.DIRECTORY_TYPE, 0775);

This example checks if the directory exists and creates it if it does not. If the directory already exists, then no action is taken. In this case, the directory is the 'pictures' subdirectory within the 'myfiles' directory on the desktop. If either the 'myfiles' directory or the 'pictures' directory do not exist, they will be created. Thus, you do not have to create a series of directories individually, the set will all be created if needed. Naturally, if the 'myfiles' directory already exists, it won't be recreated.

Iterating over the Files in a Directory

The directory object's directoryEntries (nsIFile.Attributes) attribute is used to get a list of the items in a directory. It returns an enumeration which can be iterated over.

function getLatestFile()
{
  var lastmod = 0;
  var homedir = IO.getFile("Home", "");
  var items = homedir.directoryEntries;
  while (items.hasMoreElements()) {
    var item = items.getNext().QueryInterface(Components.interfaces.nsIFile);
    if (item.isFile() && item.lastModifiedTime > lastmod.lastModifiedTime)
      lastmod = item;
  }

  return lastmod;
}

This example iterates through the files in the Home directory and looks for the file with the latest modification time (the last file in that directory that was written to). As the directoryEntries (nsIFile.Attributes) property is an enumeration, you can iterate over the items by using nsISimpleEnumerator.hasMoreElements() and nsISimpleEnumerator.getNext(). For each item, nsIFile.isFile() is called to check if an item is a file and lastModifiedTime (nsIFile.Attributes) attribute is compared with the time of the lastmod reference. If the time is higher, the lastmod variable is updated as needed. The result is that lastmod will be set to the file with the latest modification time. nsIFile.isFile() is used to ensure that the modification times are only examined for files, and not subdirectories.

But you could iterate over subdirectories as well. The following example returns an array of all of a directory's subdirectories:

function getSubdirs()
{
  var arr = [];
  var items = IO.getFile("Home", "").directoryEntries;
  while (items.hasMoreElements()) {
    var item = items.getNext();
    if (item.isDirectory())
      arr.push(item);
  }

  return arr;
}