Apollo的file I/O API
这是翻译的 Apollo for Adobe Flex Developer一书第4章Usingthe File System API
原文地址:http://www.nshen.net/blog/article.asp?id=480
简译自某本书的第4章样章,翻译不准确的请指正 :
Apollo的file I/O API允许在用户的电脑上读写文件或文件夹。
file I/O API 包含以下功能:
1 . 创建、删除文件或文件夹
2 . 复制、移动文件或文件夹
3 . 列出文件夹的内容
4 . 取得文件或文件夹的系统信息
5 . 读写二进制文件
6 . 读写文本文件
7 . Serialize and deseialize ActionScript objects
安全模型
Apollo 将提供一个完成的安全模型来管理本地资源,比如文件系统,但在Apollo alpha 1 build这个安全模型还没实现。
访问文件和目录
Apollo applications可以运行在若干平台上,包括Windows 和Mac OS . Apollo file API 使用统一平台的代码语法,所以你不需要为任何特殊的操作系统写代码。
例如,路径在Mac OS和Windows中表现是不同的,
典型的路径在Mac OS上是 /Users/joe/Documents/test.txt
在Windows上是 C:/Documents and Settings/joe/My Documents/test.txt
然而这些你都可以使用相同的Apollo组件,类,方法,属性来访问任意一个操作系统
一个ActionScript的File对象是一个文件或目录的指针
File类有一个静态属性指向用户的文档文件夹,由于不同的操作系统,所以具体的目录也不同
// On Windows: C:/Documents and Settings/joe/MyDocuments
// On Mac OS : /Users/joe/Documents
一但一个File object指向了一个目录,你就可以使用resolve()方法修改指向到一个子目录或者文件
例如下边的代码创建一个文件夹在用户文档目录:
newDir=newDir.resolve("ApolloTest");
newDir.createDirectory();
File object 可以指向一个文件或一个目录,即使这个文件或目录并不存在,就像上边的例子一样,我们指向了一个并不存在,但想去创建的一个目录。
常见目录的File Class静态属性
File.appStorageDirectory : 每个Apollo application 都被分配一个唯一的storage目录,这是一个绝好的地方存储一些这个app需要处理的,但用户不需要看到的文件,比如一些log文件,缓存文件,和一些引用的文件
File.appResourceDirectory : application被安装到的目录
File.currentDirectory : 顾名思义不翻译了
File.desktopDirectory : 同上
File.documentsDirectory : 上边讲过,是文档文件夹
File.userDirectory : 这是用户的home directory 例如在Mac OS上是 User/
在Windows上是 c://Document and Settings/username
url属性:平台无关的(platform-independent)字符串返回文件或文件夹的位置
例如:
trace(directory.url)
// on Windows: file:///C:/Documents%
// on Mac OS: file:///Users
相反,nativePath属性:返回的是Windows 或是 Mac OS 唯一平台的.
例如:下边代码指定Windows目录下的文件
file.nativePath = "c:/ApolloTest/surprise.txt";
然而一般情况下更好的方式还是使用上边提到的静态属性(例如File.appStorageDirectory)指向操作系统上已知目录,
然后使用resolve()方法创建一个相对的目录或文件,例如下边的代码
logFile = logFile.resolve("log.txt");
使用storage目录来存储你的应用程序以后需要访问的文件,但是用户并不需要知道这些。
URI scheme
file:///c:/ApolloTest/test.txt
除了这种常见的URI scheme以外还 支持2种URI scheme
app-storage :指出application的storage目录,就像下边这样
logFile = logFile.resolve("log.txt");
trace(logFile.url); // app-storage:/log.txt
app-resource :指出application的安装目录,像下边这样
installDir.url = "app-resource:/";
installDir = installDir.resolve("HelloWorld-app.xml");
trace(installDir.url); // app-resource:/HelloWorld-app.xml
不过最常见的还是
file : File对象的url属性返回一个标准的file URI scheme
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
方法的同步异步版本:
File类和FileStram类的一些方法,有同步和异步两个版本,比如File.copyFile 和File.copyFileAsync
同步的版本的方法不放弃操作直到file操作完成。异步版本的方法在后台运行,允许ActionScript过程同时发生。
直到异步文件操作完成,一个event被广播给listeners告诉他们操作完成了
这里有一个使用同步copyTo()方法来copy文件的例子
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.");
这里还有一个使用异步copyToAsync()方法来copy文件的例子
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.");
}
下边列出File类的异步方法(所有的异步方法都一个同步方法的副本)
方法 事件
copyToAsync( ) complete, ioError
deleteDirectoryAsync( ) complete, ioError
deleteFileAsync( ) complete, ioError
listDirectoryAsync( ) directoryListing, ioError
moveToAsync( ) complete, ioError
moveToTrashAsync( ) complete, ioError
当你打开一个文件,无论用FileStream对象的open()还是openAsync()方法,首先都是同步打开文件操作,然后异步打开操作。更多的信息请看这章后边的“The open( ) and openAsync( ) Methods”
当你需要在文件操作期间使用ActionScropt程序的时候(比如进度条动画)你可以使用异步版本的方法。
例如当你写一个小的文件(1兆或更小)你可以使用FileStream对象的open() (同步版本的方法)方法,但当你写的文件比较大
或不知道文件大小的时候你可以使用异步方法openAsync();
想了解更多异步方法,请看Programming ActionScript 3.0 的“Handling Events”那章
你可以在这里找到他: http://livedocs.macromedia.com/flex/2/docs/Part5_ProgAS.html
读目录的内容
File.listDirectory()方法返回指定目录的文件或文件夹的File object数组
例如下边的代码列出桌面文件夹的内容:
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");
}
}
它只会列出指定文件夹根目录的文件和文件夹,不会递归查找子文件夹。你当然也可以写代码来遍历子文件夹
但如果你真这么做了,也许最好使用File.listDirectoryAsync()方法,这样在列表的同时可以做些显示进度条之类的事了
更多请看第5章的"Getting a Directory Listing"
取得文件信息
File类包含了许多关于文件或目录的属性
属性 描述
exists 状态,这个文件或文件夹是否存在,这是个非常用有用的检查,例如,在你试图读或写或移动删除某个文件之前检查一下 是否存在
isDirectory 状态,判断这个File object是否是一个文件夹(true)还是一个文件(false)。你将在试图使用文件夹专有操作之前(例 如listDirectory()方法)检查一下这个file object是不是一个文件夹
isHidden 状态,这个文件或文件夹是否隐藏
nativePath Notes,这个文件或文件夹操作系统特有的路径(system-specific path )
parent Notes,这个File实例的父目录
url Notes,该文件或文件夹操作系统无关的路径(system-independent path).
File类还从FileReference类继承了一些有用的属性
属性 描述
creationDate 文件或文件夹的创建日期
modificationDate 文件或文件夹最后修改的日期
name 文件或文件夹的名字
size 文件大小以 bytes为单位.
复制、移动文件与文件夹
File.copyTo()和File.moveTo()方法复制或移动一个文件或文件夹到指定的新位置。例如 下边的代码复制用户文档目录子目录
Apollo Test文件夹下的test.txt 到application storage目录下的UserData子目录:
Test/test.txt");
var destination:File = File.appStorageDirectory.
resolve("User Data");
destination.createDirectory( );
var file2:File = destination.resolve("test.txt");
file1.copyTo(file2);
注意,调用File.createDirectory()方法是为了确保目标文件夹存在。
如果复制或移动操作将要很长时间,你可能需要调用File.copyToAsync()和File.moveToAsync()方法
所有的这些方法都包含一个clobber参数,你可以把这个参数设置为true来允许overwrite现有的文件,这个参数默认是false的
创建文件和文件夹
File类的File.createTempFile()和File.createTempDirectory()静态方法允许你创建一个临时的文件或文件夹。Apollo确保这个临时文件或文件夹是新的唯一的。例如下边代码创建一个临时文件:
当你关闭一个apollo application时候,临时文件和文件夹不会自动删除,所以你一般时候需要在关闭application之前删除临时文件夹。更多信息看下一节删除文件和文件夹
File.createDirectory()方法允许你在File object指定的位置上创建一个目录
directory = directory.resolve("ApolloTest");
当你打开一个可写的FileStream object时,目录自动被创建。更多内容下面
删除文件和文件夹
File.deleteFile()方法永久删除一个文件,File.deleteDirectory()方法永久删除一个文件夹。
File.moveToTransh()方法允许你移动文件或文件夹到系统的垃圾回收站
所有的这些方法也有一个异步的副本
读写文件
FileStream类提供方法来读写文件
这里是读写文件的一般过程:
1 . 建立一个File object指向这个你要读写的文件。 不会的到上边找
2 . 建立一个FileStream对象 例如
var stream:FileStream= new FileStream();
3. 调用FileStream.open()或FileStream.openAsync()方法,传递file object参数和一个fileMode参数,例如
stream.open(file,FileMode.READ)
FileMode的相关信息"文件打开模式"章节会讲到
4. 如果你调用FileStream.openAsync()方法,那么建立一个适当的监听函数。详细下边章节会讲到
5.对你的数据调用适当的读写方法。 详细下边的“读和写方法”会讲
6.关闭文件,使用FileStream.close()方法。例如: stream.close()
3,4,5步后边会详细讲,这里是一个同步读utf-8文本文件的例子
file = file.resolve("settings.xml");
var stream:FileStream = new FileStream( );
stream.open(file, FileMode.READ);
var data:String = stream.readUTFBytes(stream.
bytesAvailable);
stream.close( );
还有一个异步读同样数据的例子
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( );
}
open()和openAsync()方法
在你读或写文件之前你需要先打开这个文件
当你用FileStream.openAsync()方法打开文件的时候,这个打开是异步的,你需要注册个事件监听者来监视这个过程。
FileStream.open()方法以同步的方式打开文件,如果你的application打开文件使用同步方法,所有后续的读写操作会同步进行
下边的例子中stream.open(),stream.writeUTFBytes(), 和 stream.close()将在下次调用之前全部完成。
file = file.resolve("ApolloTest/test.txt");
68 | Chapter 4: Using the File System API
var stream:FileStream = new FileStream( )
stream.open(file, FileMode.WRITE);
stream.writeUTFBytes("This is some sample text.");
stream.close( );
这种同步操作好处在于只写较少的代码就能完成任务,坏处就是如果操作时间很长那么后续的As代码会被延时执行。所以,如果你
操作一个较大的文件,或者打开文件在较慢的网络上共享,你应该考虑使用FileStream.openAsync()方法
当你使用openAsync()方法时,下边的所有过程全部为异步的:
文件关闭(Closing the file)
当文件关闭时 FileStream object广播一个close事件
读数据到缓存(Reading data into the read buffer)
当数据被读时FileStream object广播 progress事件,然后当所有数据读完时候广播一个complete事件,一旦数据被读完后,调用一个读方法(例如 readBytes())读数据就是同步过程了
I/O 错误
FileStream object在遇到错误时候广播一个 ioError 的事件。有很多原因会出现这种情况 例如
尝试去打开一个不存在的文件或尝试写一个已经locked的文件。然而一些错误比如尝试去读的文件还没有open,将会抛出异常(而不 会广播 ioError事件)因为Apollo runtime能立刻察觉到错。
在你调用FileStream.openAsync()方法之前,你的application需要建立一些事件监听函数来处理这些他们感兴趣的事件。
下边的例子使用异步读模式打开一个文件。当这个文件被打开后,complete事件将被广播出去(除非有错误的时候会广播ioError事件)
然后completeHandler()处理函数中调用FileStream.readBytes()方法,这将开始读取文件为一个bytesArray,在异步模式,当所有的bytes都被读完后,complete事件将被广播出去:
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.");
}
文件打开模式
FileStream.open( ) 方法和 FileStream.openAsync( )方法都需要两个参数:需要打开的文件,和fileMode参数,fileMode参数是一个定义FileStream object对象能力的字符串。fileMode参数可能的值都被定义在FileMode类的衡量中了
例如,下边的代码同步打开一个文件写操作,不能读
stream.open(file, FileMode.WRITE);
这里是FileMode类的衡量以及他们的意思
FileMode.APPEND 只写模式,所有被写的数据都会附加到文件的最后 Upon opening, any nonexistent file is created.
FileMode.READ 只读模式,file必须存在 (missing files are not created).
FileMode.UPDATE 读写模式,数据可以写在文件的任何位置或者附加到尾部。Upon opening, any nonexistent file is created.
FileMode.WRITE 只写模式,如果文件不存在,将会创建新的文件,如果存在将会被覆盖
读和写方法
FileStream类包含一堆读和写的方法,每个和数据被读写的格式对应。例如,你可以使用readUTFBytes()和WriteUTFBytes ()方法读写一个bytes Array,也可以readByte()和writeByte()方法一次读写一个byte,总而言之有25个读和写的方法。详细的信息要看Apollo Alpha 1发布的ActionScript 3.0 Language Reference。
读写文本数据也许看起来价值不高,你可以在文件中编码文本readUTFBytes()和writeUTFBytes()方法提供读写UTF-8格式的文本。
readMultiByte( ) 和writeMultiByte( )方法允许你为文件指定不同的字符编码。
Thereare 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.
更多信息, 看 Apollo Developer’s Guide (http://www.adobe.com/go/apollodocs).的“Data formats, and choosing the read and write methods to use” 章节