Chapter 4. Using the File System API
Apollo provides a file I/O API that lets applications read and write files and directories on the user’s computer. The file I/O API includes the following functionality:
Create and delete files and directories
Copy and move files and directories
List the contents of directories
Get system information on files and directories
Read and write binary files
Read and write text files
Serialize and deserialize ActionScript objects
The low-level functionality for working with the file system is accessed via ActionScript. The Flex framework for Apollo includes components for working with files and directories, but these are graphical components for navigating the file system and selecting files and directories. They do not provide direct access to the more fundamental file I/O operations.
In addition to the information in this chapter, see the examples presented in "Working with the File System" in Chapter 5. Those examples illustrate many of the concepts described in this chapter, and they provide working MXML code that you can test, using Flex Builder or the Apollo SDK.
Security Model
Apollo will eventually provide a complete security model for managing access to local resources, such as the file system. However, this security model has not been implemented in the Apollo Alpha 1 build.
It is important to remember that Apollo applications are installed to and run from the user’s computer. Apollo applications have a different security context and security model than those of web browsers. Because of this, the same rules that apply to downloading and running other applications also apply to downloading and running Apollo applications. Users should download and install applications only from trusted sources.
Accessing Files and Directories
Apollo applications can run on multiple platforms, including Windows and Mac OS. The Apollo file API uses platform-neutral code syntax so you don’t have to write any OS-specific code.
For example, the way you represent a path to a file differs between Mac OS and Windows:
A typical file path on Mac OS is /Users/joe/Documents/test.txt
A typical file path on Windows is C:\Documents and Settings\joe\My Documents\test.txt
However, you can use exactly the same Apollo components, classes, methods, and properties to access files in either operating system.
An ActionScript File
object is a pointer to a file or directory. The File
class includes the static property documentsDirectory
, which contains a File
object that points to the user’s documents directory. This is the My Documents directory on Windows, and it is the Documents subdirectory of the user directory on Mac OS, as illustrated in the following code:
trace(File.documentsDirectory.nativePath) // On Windows: // C:\Documents and Settings\joe\MyDocuments // On Mac OS: /Users/joe/Documents
Once you point a File
object to a directory, you can use the resolve( )
method to modify it to point to a file or subdirectory within that directory (or within a subdirectory). For example, the following code creates an Apollo Test subdirectory of the user’s documents directory:
var newDir:File = File.documentsDirectory; newDir = newDir.resolve("ApolloTest"); newDir.createDirectory( );
A File
object can point to either a file or a directory. Also, a File
object may point to a file or directory that does not exist, as in the previous example. This lets you point a File
object to a directory location that you wish to create.
File Class Properties for Accessing Common Directory Locations
The File
class includes the following static properties, which point to commonly used directory locations:
Property |
Description |
|
Each installed Apollo application is given a unique application storage directory. This is a good place to store files that the application may want to maintain but that the user will probably need not see. This may include log files, cache files, and preferences files. |
|
The application’s install directory. |
|
This is the directory from which the file was launched. You may use this property to resolve the file path of any command-line parameters that were passed to the application. |
|
This is the user’s desktop directory. |
|
This is the My Documents directory on Windows, and the Documents subdirectory of the user directory on Mac OS. |
|
This is the user’s home directory. For example, on Mac OS, it is the Users/
|
The url and nativePath Properties of a File Object
The url
property of a File
object returns the location of a file or folder as a platform-independent string that begins with a URL scheme, such as file
, as in the following:
var directory:File = File.userDirectory; trace(directory.url) // on Windows: file:///C:/Documents%20and%20Settings // on Mac OS: file:///Users
whereas the nativePath
property of a File
object returns a string that is unique to Windows or Mac OS. For example, you can use this code to point to a specific file on a Windows computer:
var file:File = new File( ); file.nativePath = "c:/ApolloTest/surprise.txt";
However, it is generally better to start with one of the static properties listed in the table in the previous section (such as the File.appStorageDirectory
)—that point to known directories on the operating system—and then use the resolve( )
method to create to a relative path based on that directory, as in this code:
var logFile:File = File.appStorageDirectory; logFile = logFile.resolve("log.txt");
Use the application store directory to store files that you want your application to be able to access in the future but that the end user may not need to know about. For instance, this is a good place to store preferences files.
URI Schemes
A URI scheme is specified at the beginning of a URL, such as file
in the following example:
file:///c:/ApolloTest/test.txt
In addition to the file
URI scheme, Apollo supports the new URI schemes
app-storage
and app-resource
.
app-storage
Identifies the application storage directory, as shown in the following example:
var logFile:File = File.appStorageDirectory; logFile = logFile.resolve("log.txt"); trace(logFile.url); // app-storage:/log.txt
app-resource
Identifies this application’s installation folder, as in the following:
var installDir:File = new File( ); installDir.url = "app-resource:/"; installDir = installDir.resolve("HelloWorld-app.xml"); trace(installDir.url); // app-resource:/HelloWorld-app.xml
file
The url
property of other File
object returns a standard file
URL scheme:
var file:File = File.documentsDirectory;
file = file.resolve("ApolloTest/test.txt");
trace(file.url);
// On Windows:
// file:///C:/Documents%20and %20Settings/ ... /test.txt
// On Mac OS:
// file:///Users/userName/Documents/
... /test.txt
Asynchronous and Synchronous Versions of Methods
Some of the methods of the File
class (such as File.copyFile( )
and File.copyFileAsync( )
) and of the FileStream
class have both synchronous and asynchronous versions.
The synchronous methods don’t relinquish control until the file operation is complete. The asynchronous methods run in the background, allowing other ActionScript processes to take place at the same time. When the asynchronous file operation finishes, an event is dispatched to notify listeners that it is done.
Here’s an example of copying a file using the synchronous copyTo( )
method:
var file1:File = File.documentsDirectory.resolve("ApolloTest/test.txt"); var file2:File = File.documentsDirectory.resolve("ApolloTest/copy of test.txt"); file1.copyTo(file2); trace("Not output until the file is copied.");
Here’s an example of copying a file using the asynchronous copyToAsync( )
method:
var file1:File = File.documentsDirectory.resolve("ApolloTest/test.txt"); var file2:File = File.documentsDirectory.resolve("ApolloTest/copy of test.txt"); file1.copyToAsync(file2); file1.addEventListener(Event.COMPLETE, completeHandler); trace("This line executes before the complete event."); trace("So does this line."); private function completeHandler(event:Event):void { trace("Done."); }
The following table lists the asynchronous methods of the File
class (all of which have synchronous counterparts) and the events that can fire after the method is called:
Asynchronous File method |
Events |
|
|
|
|
|
|
|
|
|
|
|
|
When you open a file, use either the open( )
or openAsync( )
method of the FileStream
object. The first opens the file for synchronous operations, and the second opens the file for asynchronous operations. For more information, see "The open( ) and openAsync( ) Methods" later in this chapter.
Use asynchronous methods whenever you want to make sure that other essential ActionScript-driven processes—such as progress bar animation—continue while the file operations take place. For example, you could use the open( )
(synchronous) method of a FileStream
object if you are going to write a small file (1 MB or less) and use the openAsync( )
method when writing larger files, or when the file size is unknown.
For more information on asynchronous methods in general, see the “Handling Events” chapter in Programming ActionScript 3.0, which is available at:
http://livedocs.macromedia.com/flex/2/docs/Part5_ProgAS.html
Reading Directory Contents
The File.listDirectory( )
method returns an array listing of File
objects that represent the files and directories contained within the specified directory. For example, the following code lists the contents of the desktop directory:
var directory:File = File.desktopDirectory; var contents:Array = directory.listDirectory( ); for (var i:uint = 0; i < contents.length; i++) { if (contents[i].isDirectory) { trace(contents[i].name); } else { trace(contents[i].name, contents[i].size, "bytes"); } }
The File.listDirectory( )
method returns only the root level files and directories in a directory. It does not recursively search through subdirectories for their contents. You can, of course, write code to traverse subdirectories, though if you do so, you might want to use the File.listDirectoryAsync( )
method so that other ActionScript-driven processes can continue while the directory listings are being compiled.
Also see "Getting a Directory Listing" in Chapter 5.
Getting File Information
The File
class includes a number of properties that contain information about a file or directory.
Property |
Description |
|
States whether the file or directory exists. This is worth checking, for example, before you attempt to read, write, copy, or move a file. |
|
States whether the |
|
States whether the file or directory is hidden. |
|
Notes the operating system-specific path to the file or directory. |
|
Notes the parent directory of the File instance. |
|
Notes the operating system-independent path to the file or directory. |
The File
class also inherits the following useful properties from the FileReference
class:
Property |
Description |
|
The date the file or folder was created. |
|
The date when the file was last modified. |
|
The file or folder name. |
|
The size of the file, in bytes. |
Copying and Moving Files and Directories
The File.copyTo( )
and File.moveTo( )
methods copy or move a file or directory to a specified new location. For example, the following code copies the test.txt file in the Apollo Test subdirectory of the user’s documents directory to the User Data subdirectory of the application storage directory:
var file1:File = File.documentsDirectory.resolve("Apollo Test/test.txt"); var destination:File = File.appStorageDirectory.resolve("User Data"); destination.createDirectory( ); var file2:File = destination.resolve("test.txt"); file1.copyTo(file2);
Note the call to the File.createDirectory( )
method, which ensures that the destination directory exists.
The following code moves the Apollo Test 1 subdirectory of the user’s documents directory to the Apollo Test 2 subdirectory (effectively renaming the directory):
var dir1:File = File.documentsDirectory; dir1 = dir1.resolve("Apollo Test 1"); var dir2:File = File.documentsDirectory; dir2 = dir2.resolve("Apollo Test 2");
You might want to use the asynchronous versions of these methods, File.copyToAsync( )
and File.moveToAsync( )
, if the copy or move operation could take a long time.
Each of these methods includes a clobber
parameter, which you can set to true
to have the operation overwrite existing files. By default, this parameter is set to false
.
Creating Files and Directories
The File.createTempFile( )
and File.createTempDirectory( )
static methods of the File
class let you create a temporary file or directory. Apollo ensures that the temporary file or directory created by these methods is new and unique. For example, the following code creates a temporary file:
var bufferStorage:File = File.createTempFile( );
Temporary files and directories are not automatically deleted when you close an Apollo application. You will generally want to delete temporary files and directories before closing the application. See the next section, "Deleting Files and Directories,” for more details.
The File.createDirectory( )
method lets you create a directory in the location specified by the File
object:
var directory = File.documentsDirectory; directory = directory.resolve("ApolloTest");
When you open a FileStream
object with write capabilities then directories are created automatically, if needed. For more information about FileStream
objects, see "Reading and Writing Files" later in this chapter.
Deleting Files and Directories
The File.deleteFile( )
method permanently deletes a file, and the File.deleteDirectory( )
method permanently deletes a directory. The File.moveToTrash( )
method lets you move a file or directory to the system trash.
Each of these methods also has an asynchronous counterpart.
Reading and Writing Files
The FileStream
class provides methods that let your application read and write files.
Here’s the general process for reading and writing to a file:
Set up a
File
object that points to the file you want to read or write.For details, see "Accessing Files and Directories,” earlier in this chapter.
Instantiate a
FileStream
object—for example:var stream:FileStream = new FileStream( );.
Call the
FileStream.open( )
orFileStream.openAsync( )
method, passing in theFile
object as thefile
parameter and passing an appropriate file mode as thefileMode
parameter. For example:stream.open(file, FileMode.READ);
For more information, see "File Open Modes" later in this chapter.
If you called the
FileStream.openAsync( )
method, set up the appropriate event listener functions.For more information, see "The open( ) and openAsync( ) Methods,” next.
Call the appropriate read and write method for your data.
For more information, see "Read and Write Methods" later in this chapter
Close the file, using the
FileStream.close( )
method. For example:stream.close( );
Steps 3, 4, and 5 are described in more detail the sections that follow. First, here is a sample of code for reading UTF-8 text from a file synchronously:
var file:File = File.appStorageDirectory; file = file.resolve("settings.xml"); var stream:FileStream = new FileStream( ); stream.open(file, FileMode.READ); var data:String = stream.readUTFBytes(stream.bytesAvailable); stream.close( );
Here is some code that reads the same data asynchronously:
var file:File = File.appStorageDirectory; file = file.resolve("settings.xml"); var stream:FileStream = new FileStream( ); stream.openAsync(file, FileMode.READ); stream.addEventListener(Event.COMPLETE, readData); var data:String; private function readData(event:Event):void { data = stream.readUTFBytes(stream.bytesAvailable); stream.close( ); }
The open( ) and openAsync( ) Methods
Your application needs to open a file before it can read from or write to the file.
When you open a file with the FileStream.openAsync( )
method, the file is opened for asynchronous operations, and you’ve registered event listeners to monitor progress.
The FileStream.open( )
method opens the file for synchronous operations. If your application opens the file using this synchronous method, all subsequent calls to methods that read or write to the file will be done synchronously as well. In the following example, each of the calls to stream.open( )
, stream.writeUTFBytes( )
, and stream.close( )
will complete before the next call is made.
var newFile:File = File.documentsDirectory; file = file.resolve("ApolloTest/test.txt"); var stream:FileStream = new FileStream( ) stream.open(file, FileMode.WRITE); stream.writeUTFBytes("This is some sample text."); stream.close( );
The advantage of opening a file for synchronous operations is that you can write less code to complete a task. The disadvantage is that execution of other ActionScript code can be delayed if the file operations take a while. As a result, if you are working with large files or opening files that are shared on slow networks, you should consider using the FileStream.openAsync( )
method instead.
When you use the openAsync( )
method, the following processes are all handled asynchronously:
- Closing the file
The
FileStream
object dispatches aclose
event when the file is closed.- Reading data into the read buffer
The
FileStream
object dispatchesprogress
events as data is read, and it dispatches acomplete
event once all the data is read. However, once data is read, calling a read method (such asreadBytes( )
) to read data is a synchronous process.- I/O errors
The
FileStream
object dispatches anioError
event upon encountering an error. This may occur for a number of reasons, such as attempting to open a file that doesn’t exist or attempting to write to a file that is locked. However, some errors, such as attempting to read from a file that has not been opened, throw exceptions (rather than dispatchioError
events) because the Apollo runtime can detect the error condition instantly.
Before calling the FileStream.openAsync( )
method, your application should set up event listener functions to handle those events in which it is interested.
The following example opens a file in asynchronous read mode. After the file has been opened, the complete event will be dispatched (unless there is an error, in which case the ioError
event will be dispatched instead). The completeHandler( )
method then calls the FileStream.readBytes( )
method, which starts reading data from the file as an array of bytes, in asynchronous mode. When all the bytes have been read from the file, the complete
event will be dispatched:
var file:File = File.documentsDirectory.resolve("ApolloTest/test.txt"); var stream:FileStream = new FileStream( ); stream.addEventListener(ProgressEvent.PROGRESS, progressHandler); stream.addEventListener(Event.COMPLETE, completeHandler); stream.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); stream.addEventListener(Event.CLOSE, closeHandler); stream.openAsync(file, FileMode.READ); var data:ByteArray = new ByteArray( ); private function progressHandler(event:ProgressEvent):void { trace(stream.bytesAvailable, "bytes read."); } private function completeHandler(event: Event):void { data = stream.readBytes(stream.bytesAvailable); stream.close( ); } private function ioErrorHandler(event:IOErrorEvent):void { trace("An I/O error was encountered."); } private function closeHandler(event: Event):void { trace("File closed."); }
File Open Modes
The FileStream.open( )
method and FileStream.openAsync( )
method both accept two parameters: the file
parameter corresponding to the file that you want to open, and the fileMode
parameter, which is a string defining the capabilities of the FileStream
object. The possible values for the fileMode
parameter are defined as constants in the FileMode
class.
For example, the following code opens the file synchronously for write operations, but not for read operations:
stream.open(file, FileMode.WRITE);
Here are the FileMode
constants and their meanings:
FileMode constant |
Definition |
The file is opened in write-only mode, with all written data appended to the end of the file. Upon opening, any non-existent file is created. | |
The file is opened in read-only mode. The file must exist (missing files are not created). | |
The file is opened in read/write mode, and data can be written to any position in the file or appended to the end. Upon opening, any nonexistent file is created. | |
|
The file is opened in write-only mode. If the file does not exist, it will be created. If the file does exist, it will be overwritten. |
Read and Write Methods
The FileStream
class includes a number of read and write methods, each corresponding to the format of the data being read or written. For example, you can use the readUTFBytes( )
and writeUTFBytes( )
methods to read or write an array of bytes, whereas the readByte( )
and writeByte( )
methods read or write a single byte at a time. All in all, there are 26 read and write methods. For details on each, see the description of these methods in the ActionScript 3.0 Language Reference, which is distributed with Apollo Alpha 1.
Even though reading and writing text data may seem trivial, you should consider the encoding of the text in the file. The readUTFBytes( )
and writeUTFBytes( )
methods provide means to read and write UTF-8-encoded text. The readMultiByte( )
and writeMultiByte( )
methods let you specify a different character encoding for the file data. There are other factors to consider as well. For example, a UTF file may start with a UTF byte order mark (BOM) character, which defines the UTF encoding and the byte order (or “endianness”) of the data.
For more information, see the “Data formats, and choosing the read and write methods to use” section of the Apollo Developer’s Guide (http://www.adobe.com/go/apollodocs).
More Information
For examples of reading and writing files, see the following sections in Chapter 5:
“Writing a Text File from a String”
“Reading a Text File into a String”
“Encoding Bitmap Data into PNG or JPEG Format and Writing It to the File System”
“Serializing and De-Serializing ActionScript Objects to the File System”
Get Apollo for Adobe Flex Developers Pocket Guide now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.