辅助功能*

Jeff Swartz

Jeff Swartz

高级技术文档撰写工程师
Adobe

出版日期:
2008 年 2 月 28 日
用户级别:
中/高级
产品:
Adobe AIR

构建 JPEG 文件上载程序

图 1 所示的 PhotoUpload 范例应用程序演示 Adobe AIR 的以下文件处理功能:

  • 设置 File 对象, 使其指向某个临时文件
  • 向文件写入数据
  • 将文件上载至服务器
  • 删除文件
  • 响应 NativeApplication 对象的 exiting 事件
  • 从 JavaScript 访问 SWF 文件中的 ActionScript 类
  • 使用《Adobe AIR 的 JavaScript 语言参考》中未记载的高级运行时类 (Camera 和 Video)。

JPEG 文件上载程序

图 1。通过本范例应用程序可以上载 JPEG 文件。

注意: 本范例应用程序按原样提供, 用于教学目的。

要求

若要充分利用本篇文章, 您需要以下软件和文件:

Adobe AIR

Adobe AIR SDK

范例文件:

本范例应用程序包括以下文件:

  • index.html: 应用程序主内容, 在本文中讨论。
  • application.xml: AIR 应用程序描述符文件
  • js/Uploader.js: 包含应用程序中所使用的大量逻辑的 JavaScript 文件。代码的详细信息在第 3 节“了解代码”中进行讨论。
  • js/AIRAliases.js: AIR JavaScript 别名文件
  • swf/JPGEncoderLib.swf: 包含名为 JPGEncoder 的 ActionScript 3.0 类的 SWF 文件。
  • swf_src/com/adobe/images/swf_src/JPGEncoderLib.as: 用于编译 JPGEncoderLib.swf 文件的 ActionScript 3.0 源文件。可以使用随本范例提供的 JPGEncoderLib.swf 文件。但是, 出于安全原因, 当由第三方来源提供时, 最好根据源代码重新编译 SWF 文件。
  • swf_src/com/adobe/images/JPGEncoder.as: JPGEncoder 类所使用的实用程序类。ActionScript 3.0 Corelib 项目中也有此类。
  • BitString.as: JPGEncoder 类所使用的实用程序类。ActionScript 3.0 Corelib 项目中也有此类。
  • PHP/index.php: 要包括在支持 PHP 的 Web 服务器的 http://localhost/PhotoSyncUpload/ 文件夹中的文件。
  • AIR 图标文件范例

其它:

本应用程序要求安装网络摄像头, 且必须有权访问支持 PHP 的 Web 服务器。

必备知识

应具备构建 HTML 应用程序的一般经验。有关使用此快速入门指南的详细信息, 请参阅用 HTML 构建快速入门范例应用程序

测试应用程序

通过本范例应用程序, 可以从网络摄像头捕获图像, 并将所捕获的图像 (存储为 JPEG 文件) 上载至 Web 服务器。

Photo Uploader 使用 SWF 文件的 JPGEncoder 类将数据转换为 JPEG 格式。JPGEncoder 类发布在 ActionScript 3.0 Corelib 项目 (位于 http://code.google.com/p/as3corelib) 中。

运行应用程序 (PhotoUploadHTML.air) 之前, 在支持 PHP 的 Web 服务器的 http://localhost/PhotoSyncUpload/ 文件夹中安装 index.php 文件 (随 PHP 目录中的源文件一起提供)。此外, 请确保在计算机上安装了网络摄像头。

测试应用程序:

  1. 安装应用程序 (通过双击所提供的 AIR 文件), 并使其运行。
  2. 将网络摄像头朝向要拍摄的物体。
  3. 单击 Preview (预览) 按钮。然后, 单击 Save (保存) 按钮保存图像。
  4. 重复此步骤保存多个图像。
  5. 单击 Upload (上载) 按钮, 将文件上载至 Web 服务器

了解代码

有关使用 AIR 类的详细信息, 请参阅《为 HTML 开发人员提供的 Adobe AIR 语言参考》*

预览网络摄像头图像

Uploader.js 文件中的 appInit() 方法使用 ActionScript 3.0 文档中所记载的两个类: Camera 类和 Video 类。通过调用 Camera 类的 getCamera() 方法, 向 camera 对象 (Camera 类的实例) 分配用户计算机上的一台网络摄像头。如果没有摄像头, 应用程序将通过调用 alert() 函数通知用户。video 对象 (Video 类的实例) 是为显示摄像头所捕获图像而分配的 ActionScript 3.0 显示对象。

camera = window.runtime.flash.media.Camera.getCamera();
if (camera)
{
    video = new window.runtime.flash.media.Video();
    video.attachCamera(camera);
    camera.addEventListener(air.ActivityEvent.ACTIVITY, setSize);
}
else
{
    alert("No cameras are available.");
}

setSize() 函数设置视频的高度和宽度, 然后调用 shoot() 函数:

function setSize() 
{
    video.width = camera.width * 2;
    video.height = camera.height * 2;
    shoot(false);
}

shoot() 函数调用 flasher() 函数, 后者模拟照相机的闪光灯, 方法是快速呈现一个不透明的白色 NativeWindow 来填充屏幕:

function shoot(flash) 
{
    if (flash)
    {
        flasher();
    }
    bmd = new window.runtime.flash.display.BitmapData(video.width, video.height);
    bmd.draw(video);
    saveTemp();
}
function flasher() 
{
    var windowInitOpts = new air.NativeWindowInitOptions();
    windowInitOpts.systemChrome = air.NativeWindowSystemChrome.NONE;
    windowInitOpts.type = air.NativeWindowType.LIGHTWEIGHT;
    var flashCube = new air.NativeWindow(windowInitOpts);
    flashCube.x = 0;
    flashCube.y = 0;
    flashCube.width = air.Capabilities.screenResolutionX;
    flashCube.height = air.Capabilities.screenResolutionY;
    flashCube.visible = true;
    setTimeout(closeFlash, 100, flashCube);
}
function closeFlash(flashCube) {
    flashCube.close();
}

应用程序在初始化过程中 (在 appInit() 方法中) 首次调用 shoot 方法时, 将该方法的 flash 参数设置为 false。

然后, shoot() 方法捕获 video 对象中的图像, 并将其保存至 BitmapData 对象 bmd。有关 flash.display.BitmapData 类的介绍, 请参见 ActionScript 3.0 文档。

bmd = new window.runtime.flash.display.BitmapData(video.width, video.height);
bmd.draw(video);

保存文件

应用程序初始化时以及用户单击 Preview 按钮时, saveTemp() 方法将图像保存至 JPEG 文件, 随后将其加载至 img 图像中。

应用程序必须将 bmd 对象中包含的 BitmapData 数据转换为 JPEG 格式。corelib 项目 (位于 http://code.google.com/p/as3corelib) 中发布的 JPGEncoder ActionScript 3.0 类即可实现此功能。随 swf 目录中的源文件一起提供的 JPGEncoderLib.swf 文件包含该 JPGEncoder 类, 而本 HTML 应用程序通过使用 (index.html 文件中的) 以下脚本标签加载 SWF 文件, 使该 SWF 文件中的类可供 JavaScript 代码使用:

<script src="swf/JPGEncoderLib.swf" type="application/x-shockwave-flash"></script>

saveTemp() 函数将 JPEG 数据保存至 (通过调用 air.File.createTempFile() 创建的) 某个临时文件。通过创建 FileStream 对象并调用该对象的 writeBytes() 方法, 将数据保存至文件。然后, 将该临时文件加载至文档中的 img 图像中。通过调用 JPGEncoder 对象的 encode() 方法, 将图像数据转换为包含按 JPEG 编码的数据的 ByteArray 对象:

function saveTemp()
{
    tempFile = air.File.createTempFile();
    temps.push(tempFile);
    saveJPEG(tempFile);
    img.src = "";
    img = window.document.getElementById("img");
    img.src = tempFile.url;
    img.width = 320;
    img.height = 240;
}
function saveJPEG(file) {
    stream = new air.FileStream();
    stream.open(file, air.FileMode.WRITE);
    var data = getJPEGData(bmd);
    stream.writeBytes(data, 0, data.length);
    stream.close();
    return file;
}
function getJPEGData(bmd) {
    var jpegEncoder = new window.runtime.com.adobe.images.JPGEncoder();
    return jpegEncoder.encode(bmd);
}

用户单击 Save 按钮时, save() 方法将图像保存至应用程序资源目录的图像子目录中的 JPEG 文件。文件按 Date 对象的 time 属性所提供的时间戳来命名:

function save() 
{
    var timestamp = new Date().getTime().toString();
    var path = "images/" + timestamp + ".jpg";
    file = air.File.applicationStorageDirectory.resolvePath(path);
    saveJPEG(file);
}

将文件上载至 Web 服务器

Uploader.as 文件包含用于将文件上载至 Web 服务器的代码。已保存文件的列表存储在 files 数组 (File 对象数组) 中。upload() 方法检查应用程序存储目录的图像子目录中文件的列表:

function upload()
{
    totalSize = 0;
    files = new Array();
    var dir = air.File.applicationStorageDirectory.resolvePath("images");
    var allNodes = dir.getDirectoryListing();
    for (var i = 0; i < allNodes.length; i++)
    {
        file = allNodes[i];
        if (!file.isDirectory)
        {
            files.push(file);
            totalSize += file.size;
        }
    }
    url = UPLOAD_URL
    uploadedSoFar = 0;
    uploadNext();
}

uploadNext() 函数从 files 数组中提取一个 File 对象, 并调用 uploadFile() 函数, 该函数将上载文件:

function uploadNext()
{
    if (files.length > 0)
    {
        currentFile = files.pop();
        uploadFile(currentFile);
    }
}
function uploadFile(file)
{
    var urlRequest = new air.URLRequest(url);
    urlRequest.method = air.URLRequestMethod.POST;
    file.addEventListener(air.ProgressEvent.PROGRESS, uploadProgress);
    file.addEventListener(air.Event.COMPLETE, uploadComplete);
    file.addEventListener(air.SecurityErrorEvent.SECURITY_ERROR, uploadError);
    file.addEventListener(air.HTTPStatusEvent.HTTP_STATUS, uploadError); 
    file.addEventListener(air.IOErrorEvent.IO_ERROR, uploadError);
    file.upload(urlRequest, "uploadfile");
}

uploadFile() 函数为所上载的 File 对象设置若干事件侦听器:

function uploadProgress(event) 
{
    var uploadedAmt = uploadedSoFar + event.bytesLoaded;
    event.bytesLoaded = uploadedAmt;
    event.bytesTotal = totalSize;
    air.trace("Progress", event.bytesLoaded, event.bytesTotal);
}
function uploadComplete(event)
{
    uploadedSoFar += currentFile.size;
    var newLocation = currentFile.parent.resolvePath("uploaded/" + currentFile.name);
    uploadNext();
}
function uploadError(event)
{
    var errorStr = event.toString();
    air.trace("Error uploading: " + currentFile.nativePath + "\n  Message: " + errorStr);
}

请注意, 上载文件时调用的事件处理函数 (uploadComplete() 函数) 将调用 uploadNext() 函数以上载下一个文件 (如果有)。

在 Web 服务器上张贴的 PHP 页面将字符串视为提示, 以便将以 POST 数据提供的文件接受为要上载 (并保存在 Web 服务器上的“uploaded”子目录中) 的文件。

退出应用程序时删除临时文件

appInit() 函数为 exiting 事件设置事件侦听器, 应用程序退出时 NativeApplication 对象将调度该事件:

air.NativeApplication.nativeApplication.addEventListener(air.Event.EXITING, appExiting);

NativeApplication 对象在用户关闭主窗口时调度 exiting 事件。appExiting() 函数处理 exiting 事件, 在应用程序真正关闭之前保存会话过程中所使用的临时文件的数组:

function appExiting(event)
{
    for (i = 0; i < temps.length; i++)
    {
        tempFile = temps[i];
        tempFile.deleteFile();
    }
}

关于作者

Jeff Swartz 于 1992 年开始他在 Macromedia (现在为 Adobe Systems)的职业生涯。目前, 他是一名 Adobe AIR 技术文档撰写工程师。Jeff 拥有伊利诺斯大学乌尔班纳-香巴尼分校的计算机科学与数学专业学士学位, 并曾就读于爱丁堡大学人工智能系。旧金山湾地区附近的听众都曾“领略”过 Jeff 在长号演奏方面的艺术风采。他还曾是 Vienna Beef Ltd. 的 Big Frank (舞蹈高手)。