详细解释 使用FileReference类加载和保存本地文件

一般而言,用户不希望web浏览器中运行的应用程序访问电脑硬盘里的文件。然而,随着基于浏览器(browser-based)的富因特网应用程序的增多,一些应用程序迫切需要访问用户所选择的文件,或者将文件保存到用户所选择的位置。支持FileReference类的Adobe Flash Player版本允许ActionScript应用程序将用户所选择的文件上传到服务器,或者从服务器下载文件到用户所选择的位置。然而,在Flash Player 10之前的版本中,除非您先将数据传送到服务器,否则无法在应用程序中访问文件内容,也无法把应用程序的数据保存到本地的文件。

从Flash Player 10开始,FileReference类附加地能够打开用户选择的本地文件以及保存数据到用户选择的位置,而无需与服务器之间的文件往返。 对于能够加载本地文件以供使用,或者能够保存数据到本地文件的富因特网应用程序来说,这将大大地简化开发的过程。 为了安全起见,在Flash Player 中运行的ActionScript代码不能直接地访问本地文件系统来打开或保存文件。 应用在这一Quick Start(快速入门)里介绍的技术,这些代码通常会引出一个对话框,提示用户选择一个文件用于打开或者指定一个位置来保存文件。 另外,尽管用户已经选择了一个要打开文件,但无法确定文件所在的本地路径。 只有文件的名字是可用的。

解释示例程序

图1所示的Crop Rectangle示例程序展示了如何从用户(在自己电脑上)选择的文件中加载数据,如何在应用程序中使用那个文件,以及如何在用户电脑上创建和保存数据到一个文件。 该应用程序的工作流程如下:

  1. 当用户点击一个按钮,应用程序便会提示用户从电脑中选择一个图像文件(JPEG、PNG、或GIF)。
  2. 图像会在屏幕上显示,并伴随有一个裁剪图像的界面(选择保留图形上的一个矩形区域)。 用户还可以在两种输出格式中选择一种:JPEG 或 PNG。
  3. 当选定了所需的裁剪区域和输出的格式之后,用户单击一个按钮,会被提示指定一个保存文件的位置。 一旦用户选择了目标文件夹,该文件便会被保存,并且显示一个"save complete(保存完成)"的画面。

示例应用程序包含以下文件:

  • Crop.fla:Adobe Flash的主应用程序文件。该文件拥有用于定义大部分用户界面的映像剪辑符号。应用程序的代码在分开的ActionScript类文件之中。
  • Crop.as:主程序的源代码。这个类是FLIt的文档类,它包含有在部分用户界面之间转变的代码。它还包含了程序真正用于处理的代码——加载本地的图像文件、抓取图像所选部分的快照,以及保存数据到本地文件。
  • LoadStateView.as, ProgressStateVie>w.as, CropStateView.as, SaveCompleteStateView.as:包含了应用程序每个主要状态的用户界面代码。每个类对应于Crop.fla库里的映像剪辑符号(movie clip symbol)。(例如,LoadStateView.as定义LoadStateView类,与库符号"LoadStateView"相对应。)
  • CropControl.as:包含了一个组件的代码,该组件提供了在选择所保留图像时的用户界面。
  • BitString.as, JPGEncoder.as, PNGEncoder.as(在文件夹"com.adobe.images"):ActionScript类负责把BitmapData object(位图数据对象)中的图像数据转化为一个ByteArray(字节数组),这个ByteArray包含了特定图像文件格式(JPEG 或 PNG)的图像数据。这些类来自开源的as3corelib project,并根据New BSD license来进行发布。对于Flex来说,在mx.graphics.codec开发包里那些能够提供同样功能的类在Flex SDK中也是可用的。
  • .flashProjectProperties:用于在Flash CS4 Project panel(Flash CS4 Project仪表板)中打开程序的Flash CS4项目文件。

为了测试这个程序,您可以在浏览器中尝试使用该程序(见图1),或者通过编译源代码来运行这个示例。

 

图1. Crop Rectangle(矩形裁剪)应用程序。 (这的确是一个能在您的浏览器里运行的应用程序,而不是一个截屏。)

注意:这是一个出于指导目的而提供的示例程序。

理解代码

Crop Rectangle示例使用FileReference对象来打开和保存用户电脑上的文件。 它使用Loader 类和Bitmap类在屏幕上显示图像。 它使用BitmapData类和ByteArray类来裁剪出图像的一部分,并将它编码成数据,使其可以保存到用户电脑中。

注意:示例中也用到了其它的类,主要是显示对象和用户界面的组件,这些类定义了应用程序的用户界面。这里的Quick Start不会对用户界面的所有功能或程序的其他部分进行描述。

以下是程序打开、处理和保存文件的步骤概述:

  1. 打开用户选择的一个本地文件
    • 调用FileReference.browse()方法提示用户选择文件。当选择完毕,FileReference对象会分派一个选择事件。
    • FileReference.load()把所选的文件加载到一个变量里。当加载完毕,它会分派一个完成事件。文件数据在FileReference对象的数据属性中可用。
  2. 访问已加载的数据(在屏幕上显示图像)
    • 调用Loader.loadBytes()方法把来自FileReference.data属性的图像文件数据转化为一个Bitmap实例。当转化完毕,Loader 的contentLoaderInfo 属性中的LoaderInfo对象会分派一个完成事件。
    • 图像会显示在屏幕上,用户可以选择一个矩形区域用于裁剪输出。
  3. 捕捉并编码所裁剪的图像数据
    • 用户所选矩形中的像素快照会被BitmapData.draw()方法捕捉,并保存在BitmapData对象中。
    • 使用JPGEncoder(JPG编码)或PNGEncoder(PNG解码)类,把BitmapData对象转化为包含JPEG 或 PNG 数据的ByteArray(字节数组)。
  4. 将文件保存到用户所选择的位置
    • FileReference.save()方法的调用会提示用户选择一个保存位置。需保存的数据以参数的形式传送到save()方法。当save()操作完毕,FileReference对象会分派一个完成事件。如果操作过程存在错误,它则会分派一个ioError事件。

在这篇Quick Start余下部分中,会对这些步骤作出更详细的解释。

打开用户选择的一个本地文件

Crop Rectangle应用程序使用FileReference对象来表示用户所选择和加载的文件。在本示例中,这些代码位于Crop类之中。加载文件的过程包括两个步骤:使用FileReference.browse()方法提示用户选择一个文件,以及使用FileReference.open()方法把文件数据加载到Flash Player。每个任务都会使用事件来通知您该任务是否完成。因此,在源码中,这一过程分开在好几个方法中。

第一个是startLoadingFile()方法。在程序的第一个画面中,当用户点击按钮时,便会调用这个方法。然后,它提示用户选择打开一个文件。以下是startLoadingFile()方法的源码:

private var _loadFile:FileReference; private function startLoadingFile():void { _loadFile = new FileReference(); _loadFile.addEventListener(Event.SELECT, selectHandler); var fileFilter:FileFilter = new FileFilter("Images: (*.jpeg, *.jpg, *.gif, *.png)", "*.jpeg; *.jpg; *.gif; *.png"); _loadFile.browse([fileFilter]); }

用于选择和打开文件的FileReference对象是一个命名为_loadFile的私有实例变量(private instance variable)。因为_loadFile会在Crop类的各个方法中使用,所以它必须在整个类中都可用,于是使用了这一个实例变量。

startLoadingFile()方法处理了以下事情:

  1. 将_loadFile实例化为一个FileReference对象。
  2. 将名为selectHandler()的方法注册为选择事件的监听器(listener)。在用户被提示选择一个文件,并已经选择完毕之后,这个选择事件被分派(取消对话框的话不会分派这个事件)。
  3. 创建一个命名为fileFilter的FileFilter对象。FileFilter对象指定用户可以选择的文件类型集合;在这个示例中,可选文件的扩展名指定为.jpeg、 .jpg、.gif和 .png。它还定义了出现在对话框中的文件类型的描述:"Images: (*.jpeg, *.jpg, *.gif, *.png)"。
  4. 调用FileReference对象的browse()方法。该browse()方法会令Flash Player显示一个允许用户选择打开一个文件的对话框。

当用户选择了一个文件并在对话框中点击Open按钮之后,FileReference对象会分派出选择事件,并调用selectHandler()方法。selectHandler()方法将执行下一个步骤,file-loading(文件加载)过程:把所选的文件加载到Flash Player的内存:

private function selectHandler(event:Event):void { _loadFile.removeEventListener(Event.SELECT, selectHandler); // ... display the progress bar for the loading operation ... _loadFile.addEventListener(Event.COMPLETE, loadCompleteHandler); _loadFile.load(); }

  1. 解除自身作为选择事件的监听器(listener)。
  2. 显示加载操作的进度条(这些代码没有展示在列表中)。
  3. 将名为loadCompleteHandler()的方法注册为_loadFile对象的完成事件的监听器(listener)。当FileReference完成把文件数据从硬盘读取到内存之后,它会分派一个完成事件。
  4. 调用FileReference对象的load()方法。load()方法会启动把文件读取到代码能够访问的内存之中的过程。

当加载文件的操作完成时,_loadFile会派遣完成事件,并调用命名为loadCompleteHandler()的方法。该方法的功能将在下一节介绍。

访问已加载的的数据(在屏幕上显示的图像)

当您使用FileReference对象的load()方法从文件加载数据的时候,文件的内容会被加载到FileReference对象的数据属性(data property)。FileReference.data属性是一个包含文件原始二进制数据的ByteArray(字节数组)对象。您使用 ByteArray类的方法来读取文件的内容,并在您的程序中使用它们。例如,如果文件来自一个文字处理的应用程序,您也许希望把文本提取出来并显示到屏幕上。您甚至可以创建自定义的文件格式,并且在您的程序里加载和保存这种格式的文件。

对于Crop Rectangle应用程序,加载的文件包含图像数据,格式为JPEG、 PNG或 GIF。在这样的程序中,我们不想读文件数据中某些部分的内容;我们只想使用那些能把图像显示到屏幕的数据。为了把原始的图像数据显示到屏幕上,您要使用 Loader类的loadBytes()方法。您把数据(以ByteArray(字节数组)格式)传递到Loader对象,然后Loader对象会使用这些数据来创建一个ActionScript显示对象。它所创建的显示对象的类型是基于传递给它的数据类型。在这种情况下,由于应用程序只允许用户选择图像文件,所以Loader从这些图像文件数据创建一个Bitmap object(位图对象)。结果与您使用Loader从一个web服务器加载图像文件完全一样。与这份Quick Start中描述过的其它过程一样,Loader对象的loadBytes()方法无须即时地创建一个临时显示对象。作为取代的方法,当Loader完成把数据转化到显示对象的时候,与Loader对象对应的LoaderInfo对象会分派一个完成事件。在那时候,您就能知道显示对象可以被添加到显示列表了。

正如以上章节所描述的那样,当leReference对象完成把文件从硬盘加载到内存的时候,它会分派完成事件。在Crop Rectangle应用程序中,名为_loadFile的FileReference对象会加载用户所选择的图像文件。当加载完成时,loadCompleteHandler()方法会被调用,因为它被注册为完成事件的监听器。

然后loadCompleteHandler()方法开始把图像显示到屏幕这一过程:

private function loadCompleteHandler(event:Event):void { _loadFile.removeEventListener(Event.COMPLETE, loadCompleteHandler); var loader:Loader = new Loader(); // ... display the progress bar for converting the image data to a display object ... loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadBytesHandler); loader.loadBytes(_loadFile.data); }

loadCompleteHandler()处理以下的事情:

  1. 移除自身作为_loadFile对象的完成事件的监听器(listener)。
  2. 创建一个命名为loader的Loader实例。
  3. 显示一个进度条来展示Loader对象的loadBytes()方法的进度(没有展示出来)。
  4. loadBytesHandler()方法注册为当Loader完成创建显示对象的时候(特别地,当Loader对象的contentLoaderInfo属性的LoaderInfo对象分派出一个完成事件的时候)被调用。
  5. 调用Loader对象的loadBytes()方法,传递包含已加载文件内容的ByteArray(_loadFile.data属性)给它。

当Loader对象的loadBytes()操作完成的时候,contentLoaderInfo属性的LoaderInfo对象会分派一个完成事件。在这个示例中,它会调用loadBytesHandler()方法。下面是loadBytesHandler()方法的源码:

private function loadBytesHandler(event:Event):void { var loaderInfo:LoaderInfo = (event.target as LoaderInfo); loaderInfo.removeEventListener(Event.COMPLETE, loadBytesHandler); showImage(loaderInfo.content); }

loadBytesHandler()方法处理两件事情。首先,它移除自身作为LoaderInfo对象的完成事件的监听器。接着,它调用一个命名为showImage()的方法,把新创建的(newly-created)显示对象(loaderInfo.content属性)传递给它。

showImage()方法太大了,而不适合包含在这里,但它的工作直截了当地就是显示对象的操作任务。它创建一个命名为_imageContainer的Sprite来作为图像的容器。它装载了从所加载图像文件创建的显示对象,并通过调用_imageContainer对象的addChild()方法把它添加到显示列表(display list)。如果有必要,它还会调整图片的大小,使图片适应于程序的可视区域,并且把它放置在屏幕中央。

捕捉并编码所裁剪的图像数据

一旦用户选定了图像保留的部分和图像的输出格式(JPEG 或 PNG),然后就可以点击"Save image"按钮。 在那个时候,应用程序需要为保存剪裁图像到用户电脑做几件准备工作:

  1. 获取定义图像保留部分的矩形坐标。
  2. 截取矩形框里的像素快照,并把它保存到一个BitmapData对象。
  3. 把BitmapData对象转化为JPEG或PNG图像数据。

这些步骤与随后章节中描述的其它步骤一样,会出现在Crop类的cropAndSave()方法中。当用户点击Save image按钮的时候,这个方法会被调用。

获取剪裁矩形的坐标

首先,代码中用来获取图像选定区域对应矩形坐标的部分,使用了如下这一行代码:

var cropRect:Rectangle = _cropStateView.getCropRect();

命名为cropRectangle的 Rectangle对象包含了选定图像区域的坐标。Rectangle是通过调用_cropStateView对象的getCropRect()方法来获取的。_cropStateView对象是CropStateView类的一个实例。这个类定义了应用程序在这个时候的用户界面代码。这篇Quick Start没有描述如何计算Rectangle对象的细节;要充分注意的是,现在cropRect对象是可用的,并且它包含有选定区域的坐标。

保存剪裁图像像素的快照到BitmapData对象

下一步,创建一个命名为imageData的BitmapData对象,使用的代码如下:

var imageData:BitmapData = new BitmapData(cropRect.width, cropRect.height);

命名为imageData的BitmapData对象将会包含剪裁图像的像素数据。创建的BitmapData对象中的宽度和高度与 cropRect的Rectangle对象中的宽度和高度相匹配。因此,当像素复制到imageData的时候,只有选定矩形尺寸区域的数据被复制。

下一行代码将创建一个命名为shiftOrigin的Matrix对象。这个对象定义了原图的位置,它会成为复制到名为imageData的BitmapData对象的区域的左上角。用以下代码创建这个对象:

var shiftOrigin:Matrix = new Matrix(); shiftOrigin.translate(-cropRect.x, -cropRect.y);

Matrix对象的translate()方法引起用于像素复制操作的源图像转化为指定数目的像素。(屏幕上真正的显示对象是不会移动的——只是显示对象概念上的位置相对于像素数据快照的开始坐标。)请注意,传递到translate()方法的参数是负数。概念上,您可以把BitmapData对象想象为一个照相机,它拍下像素的快照。然而,因为照相机的位置是固定的,所以被照的图像需要移动以便捕捉指定的裁剪区域。图像必须根据Rectangle的x坐标和y坐标来上移和左移相同的数量,以便被选定的矩形的左上角与概念上的"照相机"左上角对齐。因此,translate()的参数是负数。

接着,通过调用BitmapData对象的draw()方法,把Stage上的图像像素复制到它里面:

imageData.draw(_imageContainer, shiftOrigin);

draw()方法把显示对象的像素复制到BitmapData对象。传递给draw()方法的参数如下:

  1. _imageContainer:装载着Stage上的已加载图像的Sprite(源显示对象)。
  2. shiftOrigin:如上所述,Matrix对象指定了像素快照的开始位置。

把BitmapData对象转化为JPEG或PNG数据。

名为imageData的BitmapData对象中有选定矩形的像素数据快照,那些图像数据可以转化成适当的图像格式。在Crop Rectangle应用程序示例中,有两种可选的输出格式:JPEG 和 PNG。以下代码确定了对应于这两种格式的按钮之中的哪一个会被选择。然后,这些代码会使用合适的编码器来创建一个包含有转化为指定格式图像数据的 ByteArray(encodedImage(编码图像))。

var encodedImage:ByteArray; if (_cropStateView.outputFormat == CropStateView.JPEG) { var jpgEncoder:JPGEncoder = new JPGEncoder(85); encodedImage = jpgEncoder.encode(imageData); } else { encodedImage = PNGEncoder.encode(imageData); }

"_cropStateView变量是CropStateView类的一个实例,包含了这部分程序的用户界面代码。它有一个名为outputFormat的属性,该属性值指示了所选择的输出格式选项按钮。CropStateView类也定义了两个常量,CropStateView.JPEG 和CropStateView.PNG,代表outputFormat属性可能出现的两个值。

JPGEncoder类和PNGEncoder类可以把BitmapData对象分别转化为包含这两种格式图像数据的ByteArray对象。这些类来自开源的as3corelib项目,它们是在New BSD许可证下发布的。

将文件保存到用户所选择的位置

前面的章节描述了如何将用户选定的矩形剪裁区域捕捉到BitmapData对象之中,并把它转化成名为encodedImage的ByteArray对象。下一个任务就是要把ByteArray数据以文件的形式保存到用户电脑里。与打开文件一样,保存文件也是一个多步骤的操作,通过事件来指示操作步骤是否完成。我们继续来讨论cropAndSave()方法,这些代码会在保存对话框中生成一个建议的文件名:

var fileNameRegExp:RegExp = /^(?P<fileName>.*)\..*$/; var outputFileName:String = fileNameRegExp.exec(_loadFile.name).fileName + "_crop"; if (_cropStateView.outputFormat == CropStateView.JPEG) { outputFileName += ".jpg"; } else { outputFileName += ".png"; }

在这里,我们希望默认的文件名是源文件名(不含扩展名),加上文字"_crop",再加上与所选文件格式一致的文件扩展名。这些代码使用规则表达式对象(fileNameRegExp)来获取不含扩展名的源文件名称。这个规则表达式对象的exec()方法会在任何传递进来的参数上执行规则表达式模型。在这些代码中,_loadFile.name属性会被传递到exec()方法。请记住,_loadFile是一个FileReference 对象,对应于用户在开始时所打开的源图像文件。FileReference对象的name属性包含了所选文件的全名,包括有文件扩展名。因为规则表达式模式使用了一个名为fileName的named capturing group(命名捕捉组),所以当exec()方法被调用的时候,返回的结果中有一个名为fileName的属性,包含了不带扩展名的文件名。字符串 "_crop"会连接到返回结果的末尾,然后整个文件名会保存到outputFileName变量之中。

剩下的工作就是要添加与用户选定的输出文件类型相适应的文件扩展名。条件语句会检查选择了哪种输出格式,然后添加适当的扩展名(".jpg"或 ".png")到outputFileName。

接着,代码开始进行保存JPEG或PNG图像数据到文件的操作,如下所示:

var saveFile:FileReference = new FileReference(); saveFile.addEventListener(Event.COMPLETE, saveCompleteHandler); saveFile.addEventListener(IOErrorEvent.IO_ERROR, saveIOErrorHandler); saveFile.save(encodedImage, outputFileName);

列出的代码处理以下三件事情:

  1. 它新建一个名为saveFile的FileReference对象,用于执行保存操作。
  2. 它为FileReference对象saveFile所分派出的不同事件注册监听器方法:
    • saveCompleteHandler 是完成事件的一个监听器,当用户选择了保存目标,并且Flash Player完成把文件写进硬盘的操作之后,这个完成事件会被分派。
    • saveIOErrorHandler 方法是ioError事件的一个监听器, 当Flash Player在所选位置保存文件发生错误时,这个事件会被分派。
  3. 它调用名为saveFile的FileReference对象中的save 方法。这个save 方法会打开一个对话框来提示用户选择保存文件的位置。它接受两个参数。对象传递的第一个参数是encodedImage——这个ByteArray包含有即将保存到文件的数据(在这个例子,是JPEG 或 PNG图像数据)。第二个传递的参数是outputFileName变量。这个值会在保存对话框中作为建议文件名。虽然用户可以修改文件名,但这是指定一个推荐文件扩展名的唯一方法,因此用它来提供默认名称是有好处的。

假设用户没有取消保存操作,名为saveFile的FileReference对象便开始把数据保存到硬盘上。在这个示例种,因为传递了一个ByteArray到save 方法,所以这个ByteArray的内容会被直接保存到硬盘上。如果传递到save方法的是一个不同类型的对象,那么写进文件的数据就会是不同的格式。例如,包含有String(字符串)或XML值的String(字符串)或XML对象作为一个文本文件写入。所有文件格式选项的列表,请参考ActionScript 3.0 Reference for the Adobe Flash Platform中的FileReference.save 所列。

当文件被成功保存之后,FileReference对象会分派一个完成事件。在之前的代码中,saveCompleteHandler方法被注册为这个事件的监听器,所以当文件成功保存时,FileReference对象会调用这个方法。saveCompleteHandler 方法的代码没有包含在这篇Quick Start之中。它仅仅执行了clean-up(清除)操作(移除事件的监听器),并引起用户界面显示出文件已经保存的消息。

如果在FileReference对象尝试保存文件的时候发生错误,它会分派出一个ioError事件。在那种情况下,FileReference对象会调用saveIOErrorHandler 方法。这个方法仅仅会清理事件的监听器,并显示错误消息。

 

 

文章来源:  http://www.adobe.com/cn/devnet/flash/quickstart/filereference_class_as3.html

posted @ 2014-05-01 17:31  信息技术的风采  阅读(2075)  评论(0编辑  收藏  举报