Chapter 9. Files and Directories

An application’s ability to work with the files and directories on a user’s computer is a basic necessity. Fortunately, doing so in an AIR application is generally one of the easiest things you’ll ever implement. AIR provides three simple to use classes that offer all the file and directory-related functionality you’ll ever need. In fact, there’s so much information to be covered in this area that I’ve broken it down into two separate chapters.

In this chapter you’ll learn about the fundamentals of working with files and directories. This includes allowing users to select a file or directory on their computer, accessing basic file information (its name, size, creation date, and so forth), and listing the contents of a directory. Examples will also demonstrate how to create, copy, move, delete, and trash directories and files. In Chapter 10, “Working with File Content,” you’ll learn how to write to and read from a file in different ways.

Fundamentals

As I’ve done in some other chapters, I’ll begin by covering some of the basic information you’ll need before working with files and directories. You should read these three topics before moving on to the actual examples.

AIR classes involved

When it comes to working with files and directories, there are three defined classes of note:

  • File

  • FileStream

  • FileMode

Of these, File is by far the most important. You’ll use a File object to refer to a file or a directory on the user’s computer. Once you have a File object, you can create new files and directories, move them, delete them, copy them, list their contents (for directories), and so forth. Everything is done with at least one File object.

To create a File object, you would write

var file = new air.File();

But File objects will sometimes be created without the new keyword, depending on the circumstance. You’ll see examples like this:

var file = air.File.desktopDirectory;

The next topic discusses what this particular line of code means, but for now just understand that when it comes to creating a File object, you use new when referring to just air.File() and you omit new when referring to air.File.something.

The FileStream object is used to read from or write to a file. The FileMode object contains definitions for four constants that will be used with the FileStream object. Both of these classes will be demonstrated in Chapter 10.

Paths and directories

You can reference files and directories on a user’s computer in two ways. The first is through its native path, which is an operating-system specific absolute reference. A valid native path value on Windows might be C:Documents and SettingsusernameDesktopfilename. On Mac, it might be /Users/username /Desktop/filename (replacing username with an actual username for that computer).

So if you want to explicitly state where a file is when creating a File object, you would do this:

var file = new air.File();
file.nativePath = '/path/to/file';

That code is the same as using this:

var file = new air.File('/path/to/file'),

In both cases, /path/to/file would be replaced with the actual path: for example, C:Documents and Settings ... or /Users/username .... Still, the fact is you won’t often use this method of identifying a file because /path/to/file will inevitably be operating-system-specific, thereby making your application less universal. A better way of referring to a file is to use an associated shortcut and the resolvePath() method.

When using native paths, there are five associated shortcuts for common locations that an application often references. The first three are user-related:

  • userDirectory (C:Documents and Settingsusername on Windows; /Users/username on Mac OS X)

  • documentsDirectory (C:Documents and SettingsusernameMy Documents on Windows; /Users/username/Documents on Mac OS X)

  • desktopDirectory (C:Documents and SettingsusernameDesktop on Windows; /Users/username/Desktop on Mac OS X)

Each AIR application also has two associated shortcuts. The first is applicationStorageDirectory, which points to the application’s storage directory (naturally). Where, exactly, this is on the computer depends on many conditions, but it would likely be within the C:Documents and SettingsusernameApplication Data directory on Windows and within /Users/username/Library/Preferences on Mac OS X.

The second application-related reference is to the directory where the program is installed: applicationDirectory. Note that while you might read data from the application directory (i.e., data and files that were installed with the program), you likely don’t want to write new data there. That’s what the application storage directory is for, after all.

As an example of this information, to create a File object that points to where the application was installed, use

var file = air.File.applicationDirectory;

To create a File object that points to the user’s home directory regardless of operating system, use

var file = air.File.userDirectory;

To refer to a file located within one of these locations, use the resolvePath() method. Provide it with the name of a file or directory that is the final destination, and it will return the full, absolute path to it. For example, to point to file.txt, which is on the user’s desktop, you would use

var file = air.File.desktopDirectory.resolvePath('file.txt'),

That line of code will be the same as assigning C:Documents and SettingsusernameDesktopfile.txt to file.nativePath on Windows and assigning /Users/username/Desktop/file.txt to file.nativePath on Mac OS X. But by using the shortcut and the resolvePath() method, your application will work on both operating systems without any tweaks.

As already stated, referring to an item’s native path is the first—and probably preferred—way to refer to a file or directory. The second way is to use a URL. Chapter 4 discusses the important URL schemes. For files and directories, there are three important schemes: file, app-storage, and app. The URL app-storage:/prefs.xml provides the same reference as air.File.applicationStorageDirectory.resolvePath('prefs.xml'); similarly app:/data.xml equates to air.File.applicationDirectory.resolvePath('data.xml').

The file.txt document on the user’s desktop could also be found using file:///Documents and Settings/username/Desktop/file.txt and file:///Users/username/Desktop/file.txt. (The three slashes after file: are actually two slashes—file://—followed by an implied host value of localhost, followed by the next slash; this is the same as file://localhost/Documents and Settings.)

Keep in mind that because of the differences among the various operating systems, a file reference will only be valid on a single platform (e.g., file:///Documents and Settings/username/... only works for Windows). Conversely, app and app-storage will work on all operating systems.

For the most part, you’ll use a File object and its native path value when working with files and directories. You will find the occasional situation in which some other function expects to receive a URL value for a file or directory, in which case you’ll make use of its URL scheme value.

✓ Tips

  • It’s not a problem to have spaces in your file and path names. AIR will convert those to the URL-encoded equivalent—%20—as needed.

  • Having a File object represent a file or directory that does not yet exist won’t generate errors. In fact, this is a common thing to do for applications that create files and directories. You will, however, see errors if an application attempts to read from, move, or delete files or directories that don’t exist.

  • Windows uses the backslash () to separate folders in a path, whereas Mac OS X uses the forward slash (/). You can refer to air.File.separator in an application to fetch the current operating system’s correct slash.

  • Since the backslash character has a special role in JavaScript (it’s used to escape characters to create other meanings, like which equates to a newline), you must use two backslashes to define a path on Windows:

    var file = air.File.applicationDirectory.resolvePath('resources\data.xml'),
  • The getRelativePath() method of the File object will return the relative path between two files.

Synchronicity

The third concept that you must comprehend for working with files and directories is synchronicity. Transactions, like the writing of data to a file, can be done in either synchronous or asynchronous modes. In synchronous mode, the application performs the transaction all in one burst, meaning that other tasks the application may do have to wait until the transaction is complete. In asynchronous mode, the transaction goes on behind the scenes, and the program or the user can continue to do other tasks. As an example, most Ajax processes in Web pages (or AIR applications) are asynchronous: The user can continue to scroll through the page, enter text into a form, and so on while the JavaScript is making a behind the scenes request.

In your AIR programming, you’ll need to choose between these two modes. Doing so normally involves using a different method for a task. For example, the moveToTrash() method sends the referenced file to the user’s trash. That’s the synchronous version of the method; there’s also the asynchronous moveToTrashAsync(). The most important distinction between these two modes from a programming standpoint is that the asynchronous methods require the use of event listeners that do whatever needs to be done when the transaction is occurring or completed.

So, in short, synchronous transactions are easier to program for and are perfectly fine for tasks that will take but a moment (like reading a small amount of text in from a text file). Asynchronous transactions require more programming but will result in a more polished experience for the end user when it comes to more intensive tasks. To most easily demonstrate these concepts, this chapter primarily uses the synchronous methods. Asynchronous alternatives will be discussed here and there, and in more detail in the next chapter.

File and Directory Browsing

Commonplace in most applications is the ability for users to select a file or directory from their computer. For example, users might choose a file to edit or select a directory where a file should be saved. Creating a browse for file (Figure 9.1) or browse for directory (Figure 9.2) prompt in AIR is easy. Start by creating an object of File type (in either case):

var selection = new air.File();
The browse for file dialog on Windows.

Figure 9.1. The browse for file dialog on Windows.

The browse for directory dialog on Mac OS X.

Figure 9.2. The browse for directory dialog on Mac OS X.

If you want to start the user off in a given location, use the appropriate shortcut reference without the new keyword:

var selection = air.File.userDirectory;

To create the browse for file prompt, call that method:

file.browseForFile('Prompt text'),

To create the browse for directory prompt, call that method:

file.browseForDirectory('Prompt text'),

In both cases the argument provided is the text that appears in title bar of the dialog window (see Figures 9.1 and 9.2).

Both of these methods are asynchronous (there are no synchronous alternatives). This means that event handlers must be assigned to them prior to calling browseForFile() or browseForDirectory(). The event to be watched for is SELECT:

file.addEventListener(air.Event.SELECT, selectFunction);
function selectFunction(e) {
    // Do whatever.
}

To practice using these methods, let’s create an application that starts by letting users select a file or a directory, and then reiterates to them what they selected.

To allow a user to select a file or directory

  1. In your project’s primary HTML file, create a new File object (Script 9.1):

    var file = new air.File();

    That’s all there is to it unless you’d rather start the user off in a set directory, in which case you might use

    var file = air.File.userDirectory;

    or

    var file = air.File.documentsDirectory;

    or

    var file = air.File.desktopDirectory;

    Just to be clear, this example is being created from scratch. My assumption is that you already know how to create the application.xml file and the primary HTML page (if not, see Chapter 2, “Creating an HTML Application,” and Chapter 3, “AIR Development Tools”).

    Example 9.1. This simple program reports back to users what file or directory they selected.

    1    <html><!-- Script 9.1 -->
    2       <head>
    3          <title>User Browsing</title>
    4          <script type="text/javascript" src="AIRAliases.js"></script>
    5          <script type="text/javascript">
    6
    7          // Create an object of File type:
    8          var file = new air.File();
    9
    10         // Add the event listener:
    11         file.addEventListener(air.Event.SELECT, itemWasSelected);
    12
    13         // Function that will be called
    14         // when the event occurs.
    15         function itemWasSelected(e) {
    16
    17            // Use an alert to print the selected item's name:
    18            alert ('You selected: ' + file.nativePath);
    19
    20         } // End of itemWasSelected() function.
    21
    22         // Function called when the user clicks the
    23         // 'Select a File' button.
    24         function selectFile() {
    25
    26            // Create the Open prompt:
    27            file.browseForOpen('Choose a file:'),
    28
    29         }
    30
    31         // Function called when the user clicks the
    32         // 'Select a Directory' button.
    33         function selectDirectory() {
    34
    35            // Create the Open prompt:
    36            file.browseForDirectory('Choose a directory:'),
    37
    38         }
    39
    40         </script>
    41      </head>
    42
    43   <body>
    44
    45      <button onclick="selectFile();">Select a File</button>
    46
    47      <button onclick="selectDirectory();">Select a Directory</button>
    48
    49   </body>
    50   </html>
  2. Add the event listener.

    file.addEventListener(air.Event.SELECT, itemWasSelected);

    The event to be listened for is air.Event.SELECT. When that event occurs for this object, the itemWasSelected() function will be called.

  3. Define the itemWasSelected() function:

    function itemWasSelected(e) {
      alert ('You selected: ' + file.nativePath);
    }

    This function will use an alert to report the selected item’s native path value.

  4. Define a function that prompts the user to select a file:

    function selectFile() {
      file.browseForOpen('Choose a file:'),
    }

    The application will have just two buttons (Figure 9.3). Clicking the Select a File button will call this function, which in turn calls the browseForOpen() method.

    The barebones application with two buttons. One will prompt the user to select a file, the other to select a directory.

    Figure 9.3. The barebones application with two buttons. One will prompt the user to select a file, the other to select a directory.

  5. Define a function that prompts the user to select a directory:

    function selectDirectory() {
      file.browseForDirectory('Choose a directory:'),
    }

    The other application button calls this function, which calls the browseForDirectory() method, using a different prompt as well (Figure 9.4). Other than that, allowing the user to select a file or a directory is the same process.

    The prompt for selecting a directory.

    Figure 9.4. The prompt for selecting a directory.

  6. Create the two buttons:

    <button onclick="selectFile();">Select a File</button>
    <button onclick="selectDirectory();">Select a Directory</button>

    In a real program, you’d likely call the selectFile() function using a File > Open command, but for this example, a simple button is fine.

  7. Save, test, debug, and run the completed application (Figure 9.5).

    The native path value of the selected directory is printed using an alert.

    Figure 9.5. The native path value of the selected directory is printed using an alert.

✓ Tips

  • The browseForOpenMultiple() method allows the user to select multiple files. The event to be listened for when using this method is air.FileListEvent.SELECT_MULTIPLE. In the event handling function, the file variable (or whatever you name it) would actually be an array of File objects.

  • The canonicalize() method of the File object formats the nativePath value using the correct case. For example, you can likely get away with referring to the file C:Documents and SettingsUsernameFileName as C:documents and settingsusernameFILENAME, but this method will correct the reference. It also expands shortened names on Windows to their full length.

Accessing File Information

The File object has more than two dozen properties, some of which are listed in Table 9.1. Each property provides information about the referenced file or directory. For example, as Script 9.1 shows, you can get a file’s native path (i.e., absolute path) using file.nativePath. Let’s use the information in Table 9.1 to update the last example so that it tells a little bit more about the selected item.

Table 9.1. These file and directory attributes provide common information about the item. Note that packages only exist on Mac OS X (but can be treated like directories), symbolic links are not the same as aliases or shortcuts (they’re primarily used on Unix, Linux, and Mac OS X), and the parent attribute returns a File object representing the parent folder.

File Object Properties

creationDate

When it was created

exists

If it exists

extension

What a file’s extension is

isDirectory

If it is a directory

isHidden

If it is hidden

isPackage

If it is a package (Mac OS X)

isSymbolicLink

If it is a symbolic link

modificationDate

When it was last modified

name

Its name

nativePath

Its full name and path

parent

Its parent folder

size

Its size in bytes

url

Its URL scheme value

To use a file’s information

  1. Open Script 9.1 in your text editor or IDE, if it is not already.

  2. Remove the alert() line from the itemWasSelected() function (Script 9.2).

    Example 9.2. Some of the selected file or directory’s information will be printed by this updated version of Script 9.1.

    1    <html><!-- Script 9.2 -->
    2       <head>
    3          <title>File Information</title>
    4          <script type="text/javascript" src="AIRAliases.js"></script>
    5          <script type="text/javascript">
    6
    7          // Create an object of File type:
    8          var file = new air.File();
    9
    10         // Add the event listener:
    11         file.addEventListener(air.Event.SELECT, itemWasSelected);
    12
    13         // Function that will be called
    14         // when the event occurs.
    15         function itemWasSelected(e) {
    16
    17            // Add the information to a variable:
    18            var message = 'You selected ' + file.name;
    19            message += ', located within the ' + file.parent.name + ' folder. ';
    20            message += 'It was created on ' + file.creationDate;
    21            message += '. It was last modified on ' + file.modificationDate + '.';
    22
    23            // If it's not a directory, add the file's size:
    24            if (!file.isDirectory) {
    25               message += ' It is ' + Math.ceil(file.size/1024) + 'KB in size.';
    26            }
    27
    28            // Place the message on the page:
    29            var p = document.createElement('p'),
    30            p.innerText = message;
    31            document.body.appendChild(p);
    32
    33         } // End of itemWasSelected() function.
    34
    35         // Function called when the user clicks the
    36         // 'Select a File' button.
    37         function selectFile() {
    38
    39            // Create the Open prompt:
    40            file.browseForOpen('Choose a file:'),
    41
    42         }
    43
    44         // Function called when the user clicks the
    45         // 'Select a Directory' button.
    46         function selectDirectory() {
    47
    48            // Create the Open prompt:
    49            file.browseForDirectory('Choose a directory:'),
    50
    51         }
    52
    53         </script>
    54      </head>
    55
    56   <body>
    57
    58      <button onclick="selectFile();">Select a File</button>
    59
    60      <button onclick="selectDirectory();">Select a Directory</button>
    61
    62   </body>
    63   </html>

    Instead of using an alert dialog, this version of the application will print out a bunch of information by adding a paragraph to the Document Object Model (Figure 9.6).

    The program now prints out the name, dates, and other information about the selected file or directory.

    Figure 9.6. The program now prints out the name, dates, and other information about the selected file or directory.

  3. Create a message variable, starting with the item’s name:

    var message = 'You selected ' + file.name;

    Unlike the nativePath property, which stores the absolute path to a file, the name property contains just the file or directory name. It’ll be assigned to a string variable, along with some literal text, that will later be added to the DOM.

  4. Add the item’s parent folder, creation date, and modification date to the message:

    message += ', located within the ' + file.parent.name + ' folder. ';
    message += 'It was created on ' + file.creationDate;
    message += '. It was last modified on ' + file.modificationDate + '.';

    Three more strings—combinations of literal text and file attributes—are concatenated to the message variable. Because the parent property returns a File object that points to the parent directory, its name attribute is what should be added to the string (if you add just file.parent to the message string, the value object File will be displayed).

  5. Add the file’s size to the message:

    if (!file.isDirectory) {
      message += ' It is ' + Math.ceil (file.size/1024) + 'KB in size.';
    }

    The size attribute only has a value for nondirectories (getting a directory’s size requires adding up all of its file sizes). For this reason, a conditional first checks that the file isn’t a directory. If that is true, the file’s size in kilobytes is determined by dividing its size value (which is in bytes) by 1024, and then rounding this up to the next highest integer (so that a file smaller than one kilobyte doesn’t appear as being 0KB in size).

  6. Add the message to a paragraph on the page:

    var p = document.createElement('p'),
    p.innerText = message;
    document.body.appendChild(p);

    First a new element of type p is created. Next, its innerText value is assigned the message variable (innerText is what goes between the opening and closing tags). Then this paragraph is appended to the document body.

  7. Save, test, debug, and run the completed application (Figure 9.7).

    The information for a file and a directory. Because a selected item’s information is displayed by adding a paragraph to the DOM, the page will continue to list every selected file or directory.

    Figure 9.7. The information for a file and a directory. Because a selected item’s information is displayed by adding a paragraph to the DOM, the page will continue to list every selected file or directory.

✓ Tips

  • If a file or folder is in the root directory, its parent attribute will be that root directory. The name of that parent folder will therefore be / on Mac OS X and C: (or whatever drive) on Windows.

  • If the user selects his or her root directory, file.parent.name will not have a value. To account for this possibility, you could use a conditional that prints the parent folder’s name only if it is not null.

Reading Directories

Although you can provide your AIR application with a specific directory or filename to use, or you can allow the user to select one, sometimes it’s necessary for an application to find a file on its own. For example, iTunes has the ability to look for music files on your computer.

The File object’s getDirectoryListing() and getDirectoryListingAsync() methods can be used to retrieve every file and folder within a given directory. This function returns an array of File objects, one for each item in the directory. To use it, start by creating a File object and provide it with the directory to read. You could use

var dir = new air.File('/path/to/dir');

Or, more likely, start with a common reference:

var dir = air.File.userDirectory;

Of course, you can also use code as in the previous example that allows the user to select a directory.

Next, call the getDirectoryListing() (or getDirectoryListingAsync()) method and assign the result to another variable:

var stuff = dir.getDirectoryListing();

Now stuff is an array; you can iterate through it as you would any other array:

for (i = 0; i < stuff.length; i++) {
    // Do whatever with stuff[i].
}

For this next example, the contents of a directory will be displayed in an unordered list (Figure 9.8).

This application shows the files and folders that exist on the user’s desktop. This image was taken while the program was running on Windows.

Figure 9.8. This application shows the files and folders that exist on the user’s desktop. This image was taken while the program was running on Windows.

To read a directory

  1. In your project’s primary HTML file, begin a new JavaScript function (Script 9.3):

    window.onload = function() {
    }

    I’ll start by creating a new project with its own root HTML page. Within it an anonymous function will be defined that is automatically called once the window has loaded. This is necessary because the page will add elements to the DOM, and the document’s body will only exist after the page has loaded.

  2. Within the anonymous function, get the contents of a directory:

    var dir = air.File.desktopDirectory;
    var contents = dir.getDirectoryListing();

    These two lines, already explained, are all that’s required. For this example, the target directory will be the user’s desktop. Now the contents variable is an array of File objects.

  3. If the directory isn’t empty, create an unordered list:

    if (contents.length > 0) {
      var ul = document.
    createElement('ul'),
      var li = Array();

    This conditional checks that there is at least one item in the array (i.e., in the directory). If so, a new element of type ul is created, along with an array called li. This array will be used to add each item in the directory to the unordered list.

  4. Add all visible items to the list:

    for (i = 0; i < contents.length; i++) {
      if (!contents[i].isHidden) {
        li[i] = document.createElement('li'),
        li[i].innerText = contents[i].name;
        ul.appendChild(li[i]);
      } // End of isHidden IF.
    } // End of FOR loop.

    Example 9.3. This program lists the visible files and folders found on the user’s desktop.

    1     <html><!-- Script 9.3 -->
    2        <head>
    3        <title>Directory Listing</title>
    4        <script type="text/javascript" src="AIRAliases.js"></script>
    5        <script type="text/javascript">
    6
    7        // Create an object of File type,
    8        // pointing to the user's desktop.
    9        var dir = air.File.desktopDirectory;
    10
    11       // Once the page has loaded, call this function:
    12       window.onload = makeList;
    13
    14       // Function for creating the directory listing.
    15       function makeList() {
    16
    17          // Get the directory's contents:
    18          var contents = dir.getDirectoryListing();
    19
    20          // Check that it's not empty:
    21          if (contents.length > 0) {
    22
    23             // Make an unordered list:
    24             var ul = document.createElement('ul'),
    25             var li = Array();
    26
    27             // Loop through the contents:
    28             for (i = 0; i < contents.length; i++) {
    29
    30                 // Print only visible items:
    31                 if (!contents[i].isHidden) {
    32
    33                  // Create a list item:
    34                  li[i] = document.createElement('li'),
    35                  li[i].innerText = contents[i].name;
    36                  ul.appendChild(li[i]);
    37
    38               } // End of isHidden IF.
    39
    40            } // End of FOR loop.
    41
    42            // Add the UL to the page:
    43            document.body.appendChild(ul);
    44
    45         } else { // Nothing there!
    46
    47            // Make a paragraph:
    48            var p = document.createElement('p'),
    49            p.innerHTML = 'The directory is empty.';
    50            document.body.appendChild(p);
    51
    52         }
    53
    54      } // End of makeList() function.
    55
    56      </script>
    57      </head>
    58
    59      <body>
    60
    61      <h3>What's on your Desktop?</h3>
    62
    63      </body>
    64   </html>

    This basic for loop construct will access every element within the contents array. Within the loop, a conditional checks that the current item—accessed via contents[i]—isn’t hidden. This is an optional step, but because the getDirectoryListing() method returns hidden files, showing them here could be confusing to the viewer.

    To add the item to the list, a new element of type li is added to the li array. Its innerText value will be the name of the file. This element is then appended to the unordered list. Using an array to create and add list items is necessary because reusing the same li element over and over won’t work.

  5. Add the list to the page:

    document.body.appendChild(ul);

  6. Complete the main conditional:

    } else {
      var p = document.createElement('p'),
      p.innerHTML = 'The directory is empty.';
      document.body.appendChild(p);
    }

    If the conditional in step 3 is false, the directory is empty. In that case, a paragraph is added to the page saying as much.

  7. If you want, create a heading within the page’s body:

    <h3>What's on your Desktop?</h3>
  8. Save, test, debug, and run the completed application.

    If you’d like, change the directory (to documentsDirectory or something else) and rerun the program (Figure 9.9).

    The listing of my documents directory (on Mac OS X). The heading has also been changed to match the directory in use.

    Figure 9.9. The listing of my documents directory (on Mac OS X). The heading has also been changed to match the directory in use.

✓ Tips

  • One way you could expand this example would be to add a check box indicating whether or not hidden files should be revealed. Then the conditional in the loop would read something like

    if (!contents[i].isHidden && (document.getElementById('showHidden').checked == false)) {

    You would also need to add a function that removes the unordered list and redraws it when the box is checked or unchecked. The next example shows how to recreate a list of items.

  • To use the asynchronous version of the getDirectoryListing() method, you’ll need to add an event listener to the File object. The event to be watched is FileListEvent.DIRECTORY_LISTING. Sample code for this would be:

    var dir = air.File.userDirectory;
    dir.getDirectoryListingAsync();
    dir.addEventListener(air.FileListEvent.DIRECTORY_LISTING, doList);
    function doList(e) {
      var contents = e.files;
      // FOR loop...
    }

Deleting Files and Directories

Files and directories can be deleted using the deleteFile() and deleteDirectory() methods (or the deleteFileAsync() and deleteDirectoryAsync() versions).

var file = air.File.documentsDirectory.resolvePath('somefile'),
file.deleteFile();
var dir = air.File.userDirectory.resolvePath('Some Dir'),
dir.deleteDirectory();

By default, the delete directory methods will only get rid of empty directories. To delete a directory that may have some files or subdirectories in it, pass the method an argument with the value true:

dir.deleteDirectory(true);

If you’d rather take less drastic steps, you can move the file or directory to the trash. That way the item won’t be officially removed until the user empties the trash (or Recycle Bin on Windows). The moveToTrash() and moveToTrashAsync() functions do this:

var file = air.File.documentsDirectory.resolvePath('somefile'),
file.moveToTrash();
var dir = air.File.userDirectory.resolvePath('Some Dir'),
dir.moveToTrash();

To test this new knowledge, let’s add links to the previous application that give the user the option of deleting a file or folder.

To delete a file or directory

  1. Open Script 9.3 in your text editor or IDE, if it is not already.

  2. Within the makeList() function, after creating the ul object, add an id attribute to it (Script 9.4):

    ul.setAttribute('id', 'contents'),

    To have the application reflect any changes, the program will need to be able to remove and re-create the unordered list. One way of removing a document element is to refer to its ID, so one is being added to the list here.

  3. Within the for loop, after creating the li element, add an onclick event handler to it:

    li[i].setAttribute('onclick', 'deleteItem("' + contents[i].nativePath + '");'),

    The user will be able to delete an item simple by clicking on its name. To add that functionality, onclick event handlers are added to each list item. When the item is clicked on, the deleteItem() function will be called. This function will be passed the nativePath value of the item to be deleted (so that the function knows what to get rid of).

  4. Begin a new function called deleteItem:

    function deleteItem(which) {

    This is the function that will be called when the user clicks on an item. It’s passed the item’s nativePath value, which is an absolute reference to the item on the user’s computer.

    Example 9.4. This update of Script 9.3 adds onclick event handlers to the list of items so that clicking on one will move it to the trash.

    1      <html><!-- Script 9.4 -->
    2         <head>
    3         <title>Directory Listing</title>
    4         <script type="text/javascript" src="AIRAliases.js"></script>
    5         <script type="text/javascript">
    6
    7         // Create an object of File type,
    8         // pointing to the user's desktop.
    9         var dir = air.File.desktopDirectory;
    10
    11        // Once the page has loaded, call this function:
    12        window.onload = makeList;
    13
    14        // Function for creating the directory listing.
    15        function makeList() {
    16
    17           // Get the directory's contents:
    18           var contents = dir.getDirectoryListing();
    19
    20           // Check that it's not empty:
    21           if (contents.length > 0) {
    22
    23              // Make an unordered list:
    24              var ul = document.createElement('ul'),
    25
    26              // Give the ul an ID:
    27              ul.setAttribute('id', 'contents'),
    28
    29              var li = Array();
    30
    31              // Loop through the contents:
    32              for (i = 0; i < contents.length; i++) {
    33
    34                  // Print only visible items:
    35                  if (!contents[i].isHidden) {
    36
    37                     // Create a list item:
    38                     li[i] = document.createElement('li'),
    39                     li[i].innerText = contents[i].name;
    40 
    41                     // Add an onclick event handler:
    42                     li[i].setAttribute('onclick', 'deleteItem("' + contents[i].nativePath + '");'),
    43
    44                     ul.appendChild(li[i]);
    45
    46                  } // End of isHidden IF.
    47
    48               } // End of FOR loop.
    49
    50            // Add the UL to the page:
    51            document.body.appendChild(ul);
    52
    53         } else { // Nothing there!
    54
    55            // Make a paragraph:
    56            var p = document.createElement('p'),
    57            p.innerHTML = 'The directory is empty.';
    58            document.body.appendChild(p);
    59
    60        }
    61
    62     } // End of makeList() function.
    63
    64
    65     // Function for deleting an item.
    66     // Takes one argument: the native path of the item.
    67     function deleteItem(which) {
    68
    69        // Confirm with the user prior to deleting:
    70        if (confirm('Delete ' + which + '?')) {
    71
    72           // Get a reference to the item:
    73           var trash = new air.File();
    74           trash.nativePath = which;
    75
    76           // Delete it:
    77           trash.moveToTrash();
    78
    79           // Remake the list:
    80           document.body.removeChild(document.getElementById('contents'));
    81           makeList();
    82
    83        } // End of confirm IF.
    84
    85     } // End of deleteItem() function.
    86
    87     </script>
    88     </head>
    89
    90     <body>
    91
    92     <h3>What's on your Desktop?</h3>
    93     <p>Click on an item's name to delete it.</p>
    94
    95     </body>
    96  </html>
  5. If the user definitely wants to delete the file, get rid of it:

    if (confirm('Delete ' + which + '?')) {
      var trash = new air.File();
      trash.nativePath = which;
      trash.moveToTrash();

    Rather than just immediately deleting the item, the user must click OK in a confirmation dialog (Figure 9.10). If the user clicks OK, a reference to the item is created by declaring a new File object and assigning it the provided nativePath value. Finally, the item is moved to the trash. Although I’ve been saying this script demonstrates deleting items, I’m playing it safe and just trashing each item.

    This confirmation dialog makes it clear to the user what has been selected for removal and provides one last opportunity to prevent the deletion.

    Figure 9.10. This confirmation dialog makes it clear to the user what has been selected for removal and provides one last opportunity to prevent the deletion.

    In a real application, I might be inclined to add a check that the item exists, prior to attempting to delete it:

    if (trash.exists) {...
  6. Re-create the unordered list:

    document.body.removeChild(document.getElementById('contents'));
    makeList();

    If you don’t re-create the unordered list, it will continue to show the removed items. To rebuild it, start by deleting the existing list, which is accomplished by using the removeChild() method, and then passing it the element to be removed, which is returned by getElementById(). Then call the makeList() function.

  7. Complete the deleteItem() function:

      } // End of confirm IF.
    } // End of deleteItem() function.
  8. Add instructions for the user to the body of the page:

    <p>Click on an item's name to delete it.</p>
  9. Save, test, debug, and run the completed application (Figures 9.11 and 9.12).

    The original list.

    Figure 9.11. The original list.

    The redrawn list after deleting an item (testing.txt).

    Figure 9.12. The redrawn list after deleting an item (testing.txt).

✓ Tips

  • If you’d rather not play it safe by moving an item to the trash, you can use this code instead:

    if (trash.isDirectory) {
      trash.deleteDirectory(true);
    } else {
      trash.deleteFile();
    }
  • If you’re using the asynchronous versions of the delete functions—deleteDirectoryAsync() and deleteFileAsync()—the event to be watched for is air.Event.COMPLETE. If you add an event listener for that event, when the directory has been completely deleted, your handling function will be called.

Copying and Moving

Directories and files can be copied on the user’s computer or moved from one location to another. Copying uses the copyTo() method and two valid File objects: One represents the original name and location, the second is the destination name and location. Even if you want to create a copy of a file or directory in the same directory as the original, you’ll still need two File objects (and the second will need a new name, or else you won’t get a copy).

var source = air.File.userDirectory('file.txt'),
var dest = air.File.desktopDirectory('file.txt'),
source.copyTo(dest);

The above code will copy file.txt, located in the user’s home directory, to the user’s desktop (keeping the name file.txt).

The copyTo() method takes an optional second argument, indicating whether or not an overwrite should be allowed. Assuming you have the same source and dest objects from above, this line will replace an existing file.txt on the user’s desktop:

source.copyTo(dest, true);

Moving a file is exactly the same as copying—requiring two objects with different native paths—but involves the moveTo() method:

var source = air.File.userDirectory('file.txt'),
var dest = air.File.desktopDirectory('file.txt'),
source.moveTo(dest);

That code moves file.txt from the user’s home directory to the user’s desktop. The moveTo() method also takes an optional second argument. If provided with a value of true, it will overwrite any existing item with the same name.

As a demonstration of this, the next example allows the user to select a file, select a directory, and then either copy or move the file to that directory (Figure 9.13).

The application when the user first loads it.

Figure 9.13. The application when the user first loads it.

To copy or move a file

  1. In your project’s primary HTML file, create two File objects (Script 9.5).

    var file = new air.File();
    var dir = new air.File();

    Again, I’m starting from scratch with a new project.

  2. Create two flag variables:

    var fileSelected = false;
    var dirSelected = false;

    To ensure that the copying or moving isn’t attempted until both a file and a directory have been selected, these two flag variables will track their statuses.

  3. Add event listeners to the two File objects:

    file.addEventListener(air.Event.SELECT, fileWasSelected);
    dir.addEventListener(air.Event.SELECT, dirWasSelected);

    For both objects, the event to be watched for is air.Event.SELECT. Each object has its own associated function to be called when that event happens.

  4. Define the fileWasSelected() function:

    function fileWasSelected(e) {
      document.getElementById ('fileName').innerText = file.nativePath;
      fileSelected = true;
    }

    This function will be called when the user selects a file from the hard drive. It then shows the selected file next to the corresponding button on the page (Figure 9.14). The associated flag variable is also set to true.

    After selecting the file, the file’s native path value is displayed next to the button.

    Figure 9.14. After selecting the file, the file’s native path value is displayed next to the button.

  5. Define the dirWasSelected() function:

    function dirWasSelected(e) {
      document.getElementById ('dirName').innerText = dir.nativePath;
      dirSelected = true;
    }

    This is just a slight variation on the dirWasSelected() function. The directory’s native path value will be displayed next to that button, and the matching flag variable gets set to true.

    Example 9.5. This application will move or copy a selected file to a new directory.

    1    <html><!-- Script 9.5 -->
    2       <head>
    3          <title>Copying and Moving</title>
    4          <script type="text/javascript" src="AIRAliases.js"></script>
    5          <script type="text/javascript">
    6
    7          // Create two File objects:
    8          var file = new air.File();
    9          var dir = new air.File();
    10
    11         // Flag variables:
    12         var fileSelected = false;
    13         var dirSelected = false;
    14
    15         // Add the event listeners:
    16         file.addEventListener(air.Event.SELECT, fileWasSelected);
    17         dir.addEventListener(air.Event.SELECT, dirWasSelected);
    18
    19         // Function called when the file is selected.
    20         function fileWasSelected(e) {
    21
    22            document.getElementById('fileName').innerText = file.nativePath;
    23            fileSelected = true;
    24
    25         } // End of fileWasSelected() function.
    26
    27         // Function called when the directory is selected.
    28         function dirWasSelected(e) {
    29
    30            document.getElementById('dirName').innerText = dir.nativePath;
    31            dirSelected = true;
    32
    33         } // End of dirWasSelected() function.
    34
    35         // Function called when the user clicks the
    36         // 'Select a File' button.
    37         function selectFile() {
    38
    39            // Create the Open prompt:
    40            file.browseForOpen('Choose a file:'),
    41
    42         }
    43
    44         // Function called when the user clicks the
    45         // 'Select a Directory' button.
    46         function selectDirectory() {
    47
    48            // Create the Open prompt:
    49            dir.browseForDirectory('Choose a directory:'),
    50
    51         }
    52
    53         // Function called when the user clicks Go!
    54         function doCopyMove() {
    55
    56            // Make sure that both items have been selected:
    57            if (fileSelected && dirSelected) {
    58
    59               // Determine the destination:
    60               var dest = new air.File();
    61               dest.nativePath = dir.nativePath + air.File.separator + file.
    62
    63               // Copy or move:
    64               if (document.getElementById('copyMove').value == 'copy') {
    65                  file.copyTo(dest);
    66                  alert('The file has been copied!'),
    67               } else {
    68                  file.moveTo(dest);
    69                  alert('The file has been moved!'),
    70                  fileSelected = false;
    71               }
    72
    73            } else { // Missing something!
    74               alert('You must select a file and a directory first!'),
    75            }
    76
    77         } // End of doCopyMove() function.
    78
    79         </script>
    80      </head>
    81
    82   <body>
    83
    84      <h3>Copy or Move a File</h3>
    85
    86      <p><button onclick="selectFile();">Select a File</button> <span id="filename"></span></p>
    87
    88      <p><button onclick="selectDirectory();">Select a Directory</button> <span id="dirname">
            </span></p>
    89
    90      <p><select id="copyMove"><option value="copy">Copy</option><option value="move">Move
            </option></select><button onclick="doCopyMove();">Go!</button></p>
    91
    92   </body>
    93   </html>
  6. Define a function for selecting a file:

    function selectFile() {
      file.browseForOpen('Choose a file:'),
    }

    This function will be called when the user clicks the Select a File button. It generates the browse for open dialog (see Figure 9.1).

  7. Define a function for selecting a directory:

    function selectDirectory() {
      dir.browseForDirectory('Choose a directory:'),
    }

    This function will be called when the user clicks this Select a Directory button. It generates the browse for directory dialog (as in Figure 9.2).

  8. Begin the doCopyMove() function:

    function doCopyMove() {
      if (fileSelected && dirSelected) {

    This is the most important function, because it performs the actual copying or moving. The function will be called when the user clicks the Go! button (see Figures 9.13 and 9.14). Within the function, a conditional confirms that both the file and directory have been selected (by referring to the flag variables).

  9. Identify the destination for the file:

    var dest = new air.File();
    dest.nativePath = dir.nativePath +
    air.File.separator + file.name;

    One more File object is necessary to identify the destination. That native path value will be the nativePath of the selected directory, plus the operating-system-specific separator (a forward or backward slash), plus the selected file’s name.

  10. If appropriate, copy the file:

    if (document.getElementById('copyMove').value == 'copy') {
     file.copyTo(dest);
    alert('The file has been copied!'),

    The user can indicate the action to be performed—copy or move—using the select menu. If the user chooses copy (the default option), the copyTo() method is called and an alert informs the user of the action (Figure 9.15).

    The result after copying the file to the destination.

    Figure 9.15. The result after copying the file to the destination.

  11. Otherwise, move the file:

    } else {
     file.moveTo(dest);
     alert('The file has been moved!'),
     fileSelected = false;
    }

    Besides using the moveTo() function, instead of copyTo(), the other significant alteration here is the resetting of the fileSelected variable’s value to false. This is necessary because the file no longer exists in the same location after a move, so its reference is inaccurate. By setting fileSelected to false, the user will need to select a new file (even if that means the same file in its new location) prior to copying or moving again.

  12. Complete the doCopyMove() function:

    } else { // Missing something!
      alert('You must select a file and a directory first!'),
     }
    } // End of doCopyMove() function.

    If the user clicks Go! before selecting both a file and a directory, the use will see this alert (Figure 9.16).

    This alert is shown if the user tries to copy or move a file without having selected both a file and a directory.

    Figure 9.16. This alert is shown if the user tries to copy or move a file without having selected both a file and a directory.

  13. Within the body of the page, create the necessary buttons:

    <p><button onclick="selectFile();">Select a File</button> <span id="fileName"></span></p>
    <p><button onclick="selectDirectory();">Select a Directory</button> <span id="dirName"></span></p>

    The first button invokes the selectFile() function; the second, the selectDirectory() function.

  14. Add the select menu and the Go! button:

    <p><select id="copyMove"><option value="copy">Copy</option><option value="move">Move</option></select><button onclick="doCopyMove();">Go!</button></p>
  15. Save, test, debug, and run the completed application.

✓ Tips

  • Note that this application will not perform the move if a file with the same name already exists in that destination. To account for that possibility, you could add an event listener that watches for an IOError or call the moveTo() method within a try...catch block.

  • The clone() method duplicates the existing File object, giving you two references to the same file:

    var file = air.File.userDirectory('file.txt'),
    var file2 = file.clone();
  • To rename a file, use the moveTo() method, but leave the directory the same and change the name value:

    var source = air.File.userDirectory('file.txt'),
    var dest = air.File.userDirectory('newname.txt'),
    source.moveTo(dest);
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
98.82.120.188