Aug
12

Adobe's new Integrated Runtime (AIR) will be released at some point this year (a beta is currently available here). The release of AIR will bring new possibilities in how applications communicate with client machines. Previous to the Adobe Integrated Runtime, Flash applications adhered to a security model that drastically handicapped what your applications could and couldn't do. For more information on the general Flash player security model see this page; for more info on Flash player security and Flex 2.01 see this page. With AIR, all of the following are now possible:

The ability to:

  • create files and directories
  • list the contents of directories
  • copy and move files and directories
  • read and write text and binary files
  • serialize and deserialize ActionScript objects and classes to the file system
  • get system information on files and directories

In this blog post I'll talk about what new classes are available in Flex 3 (Flex "Moxie" M2 SDK to be specific) that make all this possible. In my next post I'll show an example of how to use the file system API in a real-world application.

A new package is included in Flex 3 called flash.filesystem and it includes the following classes: File, FileMode, and FileStream. Let's take a closer look at each of these new classes.

flash.filesystem.File
The file class is a representation of a file or directory on the file system. It's also used to represent a file or directory that you wish to create. Included in the File class are properties and methods for getting information about the file system and for performing operations on the file system like creating or deleting files and directories. Coupling the File class with the FileStream class (which we'll examine in a minute) you can read and write binary and text files on the client machine.

The first important aspect of the File class is how it abstracts file system paths for you making it easier to write applications. If you remember, AIR is a cross-platform runtime available on Windows, Macintosh and - in the future - Linux. These operating systems represent file and directory paths in totally different ways. For example, my home directory on Windows might be C:\Documents and Settings\aaron\ while on a Mac it would be /Users/aaron/. The File class has several static properties that represent operation system agnostic paths.

File.appStorageDirectory
Every AIR application is given a storage directory. This is a great place to store preference files, cache files, log files, or any other file specific to the application running on the client. Example application storage directories are /Users/aaron/Library/Preferences/[AIR_application_ID]/Local Store/ for Mac and C:\Documents and Settings\aaron\Application Data\[AIR_application_ID]\Local Store\ for Windows where [AIR_application_ID] is the unique ID of your application. As an example, the application ID for my twitterAIR app is com.trajiklyhip.twitterAIR. This ID is set by the developer in the AIR application descriptor file.

File.appResourceDirectory
This is the directory where an AIR application is installed. For instance /Users/aaron/Applications/ on Mac and C:\Documents and Settings\aaron\Local Settings\Application Data\[AIR_application_name]\ for Windows.

File.userDirectory
The base directory for the user. Example: /Users/aaron/ on Mac and C:\Documents and Settings\aaron\ on Windows. This directory serves as the base directory for all subsequent directories described below.

File.desktopDirectory
The users desktop directory. Example: /Users/aaron/Desktop/ on Mac and C:\Documents and Settings\aaron\Desktop\ on Windows.

File.documentsDirectory
The users documents directory Example: /Users/aaron/Documents on Mac and C:\Documents and Settings\aaron\My Documents\ on Windows.

File.currentDirectory
This is the directory where the AIR application was launched. Note, users can move applications from their initial location to other locations on the file system. Thus, the value of this static property may differ from the value stored in File.appResourceDirectory.

In order to make use of these static properties you must first create an instance of the File class. For example, to create an instance of the File class that points to the users documents directory you would write the following code:

import flash.filesystem.File;

var docsDirectory:File = File.documentsDirectory;

Once you have an instance of the File class created there are several properties of the instance that describe the file or directory the instance references. These include:

File.isDirectory
Whether the instance refers to a directory (true) or a file (false). Checking the value of this property is useful before calling any directory specific functions like File.listDirectory(), which returns the list of files and directories stored in the File instance.

File.isHidden
Whether the instance refers to a hidden directory or file on the file system.

File.exists
A boolean value indicating whether the resource exists. You should check this value before attempting to write or read a specified file or directory.

File.nativePath
This property reveals the operating system specific path to a file or directory. From the example code above (File.documentsDirectory) this value might be /Users/aaron/Documents on Mac and C:\Documents and Settings\aaron\My Documents\ on Windows.

File.url
This property is the reverse of File.nativePath and reveals the operating system independent path to a file or directory preceded by a URL scheme. From the example code above this value might be file:///Users/aaron/Documents on Mac and file://C:/Documents%20and%20Settings/aaron/My Documents/ on Windows.

File.parent
The parent directory (as an instance of File) of the File instance.

Other useful properties of a File instance include: File.creationDate, File.modificationDate, File.name, File.size, File.type, File.creator, and File.extension. Note, some of these are only valid for instances of the File class that point to files.

Returning to the previous example, what if you wanted to trace the value of the user's documents directory? You can't just write trace(docsDirectory) as docsDirectory is a File instance, and not a string. Using the properties described above, you could write the following:

import flash.filesystem.File;

var docsDirectory:File = File.documentsDirectory;
trace(docsDirectory.url);
// file:///Users/aaron/Documents

If you wanted to create a reference to an actual file in the user's documents directory and determine whether it exists or not you would write:

import flash.filesystem.File;

var myFile:File = File.documentsDirectory.resolve("MyFile.txt");
trace(myFile.exists);
// true or false depending on whether the file exists or not

flash.filesystem.FileMode
The second class I want to discuss today is the FileMode class. This class relates to various modes with which a file can be opened. In ColdFusion, this type of low-level access is mostly hidden. In fact, you don't even have to explicitly open and close files in order to work with them. In AIR, you must perform these steps. When it comes time to open a file you'll need to decide how you wish to open the file based on what you are going to be doing with it. Here are your options - which are constant values (hence the upper case) of the FileMode class. You use these constants in conjunction with the FileStream class (covered below) when opening files.

FileMode.READ
The file is opened in read-only mode and must already exist in the given location. An File I/O exception will be thrown if you attempt to open a non-existing file in FileMode.READ mode.

FileMode.WRITE
The file is opened in write-only mode. If the file does not exist in the given location it will be created. If the file does exist, it will be completely overwritten.

FileMode.UPDATE
The file is opened in read/write mode. Data can be written to any position in the file or appended to the end. If the file does not exist it will be created automatically.

FileMode.APPEND
The file is opened in write-only mode and data is automatically written to the end of the file. If the file does not exist it will be created automatically.

flash.filesystem.FileStream
The FileStream class is used to read and write files. It's where all the real heavy-lifting is done in terms of opening connections to files, writing to those files, and closing the files. Included in the class are some 26 methods for reading and writing text files, binary files, or ActionScript objects to files. The type of read/write method you choose will determine how data is encoded in files as well as whether the reading/writing actions are performed synchronously or asychronously. For example, the FileStream.readUTFBytes() and FileStream.writeUTFBytes() methods read and write UTF-8 encoded text respectively. If you want to specify a different character encoded, the Filestream.readMultiByte() and FileStream.writeMultiByte() methods are available. These last two methods take two arguments, the second of which is the character encoding you want to use. Here are two examples of writing UTF-8 encoded data into a file. The first does so synchronously and the second asynchronously. Which method you choose should be based on your application strategy but a good rule is write large files - that may take a long time to write - asynchronously. This will allow your application to continue functioning without waiting on the file to write to the file system.

Write UTF-8 encoded string data to a file on the user's desktop synchronously.

import flash.filesystem.*;

// Create a reference to MyFile.txt on the user's desktop.

var myFile:File = File.desktopDirectory.resolve("MyFile.txt");

// Create a new FileStream instance.

var fs:FileStream = new FileStream();

// Create a String we'll use to write inside the file.

var myString:String = "Here's a string of text.";

// Open MyFile.txt in write-only synchronous mode.

fs.open(myFile, FileMode.WRITE);

// Write the string to the file synchronously.

fs.writeUTFBytes(myString);

// Close the file.

fs.close();

Write UTF-8 encoded string data to a file on the user's desktop asynchronously.

import flash.filesystem.*;
import flash.events.Event;

// Create a reference to MyFile.txt on the user's desktop.

var myFile:File = File.desktopDirectory.resolve("MyFile.txt");

// Create a new FileStream instance.

var fs:FileStream = new FileStream();

// Add the function fileWrittenComplete as a close listener to the file stream.

fs.addEventListener(Event.CLOSE, fileWrittenComplete);

// Create a String we'll use to write inside the file.

var myString:String = "Here's a string of text.";

// Open MyFile.txt in write-only asynchronous mode.

fs.openAsync(myFile, FileMode.WRITE);

// Write the string to the file asynchronously.

fs.writeUTFBytes(myString);

// Close the file.

fs.close();

// Event handler for when the file has been written.

public function fileWrittenComplete(event:Event):void {
    trace("MyFile.txt has been written to the file system.");
}

In addition to writing textual data to the file system, the FileStream class allows you to write binary data (such as a JPEG) or a full ActionScript class or generic Object to the file system. The overall File API included in Flex 3 is relatively easy to work with and extremely powerful. Using it properly opens your desktop applications to a whole new realm of possibilities. Stay tuned in the near future to read more about real-world ways to utilize these new classes in your applications.

Aaron West's Gravatar
About this post:

This entry was posted by Aaron West on August 12, 2007 at 4:07 PM. It was filed in the following categories: Adobe AIR, Flex. It has been viewed 54501 times and has 14 comments.

14 Responses to Working With the File System API in AIR

  1. freska

    Hi, nice post. Too bad, I can't try it out. I've installed adobe AIR 1.0, but my Eclipse doesn't seem to recognize File System API. When I tried typing "import flash.filesystem.File", it says "undefined property File". Do you have any idea how to fix this? I use Eclipse 3.3 and Flex Builder 3. Thanks.

  2. Hi freska, based on your comments I take it you are running the plugin version of Flex Builder 3. From what I can tell, the plugin is only supported in the Eclipse 3.2 environment. Here's what I found on Adobe's site that leads me to believe this:

    (text below taken from the downloads page for Flex Builder 3 Beta)

    Flex Builder 3 Beta - Eclipse Plugin
    This download provides the Flex Builder 3 beta as an Eclipse plugin that can be used on either Macintosh or Windows platforms. The prerelease version of the Flex 3 SDK is included with this prerelease Flex Builder 3 download. Eclipse version 3.2.1 or higher is required in order to install and use this version of Flex Builder 3 beta.

  3. freska

    Yes, I am running the plugin version of Flex Builder 3. In fact, I'm also running the standalone version (Flex IDE). I tried downgrading to Eclipse 3.2, but that error is still being displayed. I find this from Adobe Flex 2 language Reference, "The flash.filesystem package contains classes used in accessing the filesystem. This package is only available to content running in the Adobe Integrated Runtime." Did I miss something related to the software spec or so?

  4. freska

    Haha.. it's my mistake. I did that on a Flex project, not AIR. You can delete my 2nd comment. Now, my Eclipse recognizes the "import flash.filesystem.File", but I still got the same error message. Say, when I tried your example #5, Eclipse says "undefined property fs" on "fs.close()". It also happens to other variables (myFile, myString) and the event handler method (fileWrittenComplete). Did I miss something here? Thanks.

  5. Glad you figured out the issue between a Flex project versus an AIR project. As far as the code errors go, the error messages indicate the variables are not defined - but you already know that. Exactly why the compiler thinks they are not defined is anyone's guess at this point. If you post the code you are using I might be able to help further.

    Lastly, in looking at my final example above (asynchronously writing a file) there may be an issue with the fs.close(); method call. Since the file stream is opened asynchronously the close() method may be getting called out of turn. It would be best to move the fs.close() into the fileWrittenComplete function (assuming all we wanted to do was write to the file and nothing else). If we were going to perform several write actions on the file, the fs.close() method could be strategically placed somewhere else.

  6. freska

    Hi Aaron, sorry for taking so long. I had another task to complete.

    Well, I just put your code right away inside the <mx:Script> tag without adding anything. The errors, saying something about undefined property, show up when I save the file. Now, I try putting the code inside a function and have it called when a button is clicked. The errors are gone and a file is created successfully on my desktop.

  7. Jesse

    I am developing my first Flex application, and I don't know anything about AIR.

    You say that "A new package is included in Flex 3 called flash.filesystem," yet I am trying to compile a Flex 3.2 application that references this package per your synchronously example and I get compiler errors suggesting the types are not found:

    Error: Type was not found or was not a compile-time constant: File.
    var docsDirectory:File = File.documentsDirectory;

    Error: Type was not found or was not a compile-time constant: File.
    var myFile:File = File.desktopDirectory.resolve("MyFile.txt");

    Error: Type was not found or was not a compile-time constant: FileStream.
    var fs:FileStream = new FileStream();

    Error: Access of undefined property File.
    var docsDirectory:File = File.documentsDirectory;

    Error: Definition flash.filesystem could not be found.
    import flash.filesystem.*;

    Error: Access of undefined property File.
    var myFile:File = File.desktopDirectory.resolve("MyFile.txt");

    Error: Call to a possibly undefined method FileStream.
    var fs:FileStream = new FileStream();

    Error: Access of undefined property FileMode.
    fs.open(myFile, FileMode.WRITE);

    How do I include the flash.filesystem package (beyond adding the line "import flash.filesystem.*;")? Should I be trying to compile an AIR application? If so, how do I do that?

  8. @Jesse - Are you working with a Flex project or an AIR project? If you are using the standalone Flex Builder 3 or the Flex Builder 3 Eclipse plugin, you can either create a Flex project or an AIR project.

    Only AIR projects have the ability to import the flash.filesystem package. Reading from and writing to the local file system is a feature in desktop (AIR) Flex applications only due to security restrictions in browser-based applications.

    In Flex Builder 3 and Eclipse each project will have an icon that tells you what type of project it is. AIR projects will have an AIR icon and Flex projects will have a Flex icon.

  9. bersy

    Hi Aaron, flash.filesystem.File class has Icon property, could you please advice how to use this one to display file's icon?

  10. @bersy - From the Flex 4 SDK documentation I found the following:

    An Icon object is an array of BitmapData objects corresponding to the various icon states. On Linux, the Icon object contains no icons. The following code shows how to find the image in the icon array that has the greatest height, and it sets a Bitmap object to that image.

    import flash.filesystem.File;
    import flash.display.*;

    var directory:File = File.documentsDirectory;
    var bitmaps:Array = directory.icon.bitmaps;
    var bmpData:BitmapData = new BitmapData(1, 1);
    for (var i:uint = 0; i < bitmaps.length; i++) {
    if (bitmaps[i].height > bmpData.height) {
    bmpData = directory.icon.bitmaps[i];
    }
    }
    var iconBmp:Bitmap = new Bitmap(bmpData);

    You might add this Bitmap object as a child of a display object container, such as a Sprite object or a Flex UIComponent object.

  11. bersy

    Thanks for the quick response. I certainly saw that example, but when I added a Bitmap using the addChild (as described in example) an error was occured. It turned out that the need using rawChildren.addchild, then everything works.

  12. khushwant

    Hi,

    I have created an AIR application using flash cs5.In that application when user clicks on download button files get copied to user's desktop from application directory. Its working fine on windows but when i test it on mac it is doing all the other task but when user clicks on download button then it is not copying the files from application directory to user's desktop.

    Please reply as soon as possible.

  13. khushwant

    Everything i am doing is locally. and application directory means USB drive(pen drive) because i have AIR installation folder in USB drive along with all other files(which needs to get copied to user's desktop when user clicks on download button)

  14. Sonic

    So you mean "AIR" app is AIR app created by "Flash Builder", instead of AIR app created by "Flash CS6" ?
    No way to use "FileStream" class in cs6?