BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

从CameraRoll和CameraUI上传图像

| 作者 Joe Ward 关注 0 他的粉丝 发布于 2011年9月3日. 估计阅读时间: 19 分钟 | Google、Facebook、Pinterest、阿里、腾讯 等顶尖技术团队的上百个可供参考的架构实例!

目录

需求

预备知识

使用Flash Builder、Flash Professional或建议的其他工具构建AIR应用程序的一般经验。

用户水平

中级

需要的产品

示例文件

本文探讨如何通过CameraRoll和CameraUI类将获得的图像上传到服务器。

CameraRoll类可用于打开一个图像浏览器,以便用户可从设备媒体库选择照片。CameraUI类可用于启动设备照相机应用程序,以便用户可拍摄照片或录制视频。在两种情况下,CameraRoll和CameraUI对象都通过一个包含媒体承诺的事件来返回媒体。

可用于访问媒体承诺中的数据的选项在各个平台中不尽相同。例如,在Android上,媒体承诺包括一个引用源图像文件file属性,但在iOS上,此属性始终为空——源文件不可访问。本文中描述的技术适用于所有平台。

获取媒体承诺

第一步是从CameraRoll或CameraUI对象请求图像。在两种情况下,您都会创建合适的对象,设置一些事件监听器,然后调用请求图像的函数。然后,在用户从设备媒体库选择一张照片或使用照相机拍摄一张新照片之后,运行时在一个包含媒体承诺的事件中返回图像。

CameraRoll

下面的代码示例设置必要的事件监听器来处理可由CameraRoll对象分配的事件,然后调用browseForImage()方法。当用户选择一张图像时,运行时调用一个名为imageSelected的事件处理函数。

//declare cameraRoll where it won't go out of scope
var cameraRoll:CameraRoll = new CameraRoll();
 
if( CameraRoll.supportsBrowseForImage )
{
    cameraRoll.addEventListener( MediaEvent.SELECT, imageSelected );
    cameraRoll.addEventListener( Event.CANCEL, browseCanceled );
    cameraRoll.addEventListener( ErrorEvent.ERROR, mediaError );
    cameraRoll.browseForImage();
}
else
{
    trace( "Image browsing is not supported on this device.");
} 

CameraUI

从CameraUI对象请求图像的代码非常类似:

private var cameraUI:CameraUI = new CameraUI();
if( CameraUI.isSupported )
{
    trace( "Initializing..." );
    cameraUI.addEventListener( MediaEvent.COMPLETE, imageSelected );
    cameraUI.addEventListener( Event.CANCEL, browseCanceled );
    cameraUI.addEventListener( ErrorEvent.ERROR, mediaError );
    cameraUI.launch( MediaType.IMAGE );
}
else
{
    trace( "CameraUI is not supported.");
}

要注意的一点区别是,CameraRoll分派一个类型为 MediaEvent.SELECT的事件,而CameraUI类分派一个类型为MediaEvent.COMPLETE的事件。

获得原始图像数据

您可以从媒体承诺以字节数组的形式获取原始图像数据。但媒体承诺对象可以以一些不同方式提供自己的数据。所以,强健的跨平台解决方案的创建需要费一定的工夫。在Android上,CameraRoll和CameraUI分派的媒体承诺包括一个引用图像文件的File对象。但是,File对象在iOS上不可用。如果您的代码直接打开文件,它将仅适用于Android。

如果您只希望显示图像,可以始终使用Loader.loadMediaPromise()方法。此方法加载图像并将它解码为一个显示对象。但要上传图像,您需要一个包含图像数据的File对象或ByteArray对象,而不是Flash显示对象。您可以使用MediaPromise对象的file属性(如果可用),但当file属性不可用时,可以通过读取媒体承诺数据源来访问图像数据。

媒体承诺对象提供了对可能同步或异步的数据源的访问。在同步情形下,您可以打开数据源并立即读取数据。但在异步情形下,在数据可用之前必须等待事件进展或完成事件。

下面的代码示例检查媒体承诺数据是同步的还是异步的(使用isAsync属性)。在异步情形下,该示例添加一个事件监听器。因为您不知道实际数据源的对象类型是什么,所以必须使用“as”运算符将它转换为合适的接口。将对象转换为IdataInput接口,以从数据源读取数据。将他转换为IeventDispatcher接口,可以访问异步数据源的addEventListener()方法。

private var dataSource:IDataInput;
private function imageSelected( event:MediaEvent ):void
{
    trace( "Media selected..." );      
    var imagePromise:MediaPromise = event.data;
    dataSource = imagePromise.open();
         
    if( imagePromise.isAsync )
    {
        trace( "Asynchronous media promise." );
        var eventSource:IEventDispatcher = dataSource as IEventDispatcher;            
        eventSource.addEventListener( Event.COMPLETE, onMediaLoaded );         
    }
    else
    {
        trace( "Synchronous media promise." );
        readMediaData();
    }
}
      
private function onMediaLoaded( event:Event ):void
{
    trace("Media load complete");
    readMediaData();
}
      
private function readMediaData():void
{
    //do something with the data
}

上传图像

上传图像的最简单方式是使用FileReference.upload()。是的,我知道我刚才告诉您不要依靠文件的存在与否。但是,如果没有从媒体承诺直接获得文件,可以自行创建一个临时文件。使用FileReference.upload()的另一个优势是,它执行一种多部分表单样式上传。许多服务器端上传脚本要求这种类型的上传,因为允许用户上传文件的HTML表单常常使用它。

另一个选项是使用URLLoader类直接上传字节数组。以原始字节数组形式执行上传也很简单。但是,如果您的服务器脚本希望采用多部分表单上传,您将必须编写代码来创建对恰当格式的请求。除了FileReference.upload(),没有任何ActionScript API可为您提供直接帮助。您可以使用一些第三方库,比如Eugene Zatepyakin的开源MultipartURLLoader类。

FileReference.upload()

下面的示例演示了如何创建和上传临时文件:

private function readMediaData():void
{
    var imageBytes:ByteArray = new ByteArray();
    dataSource.readBytes( imageBytes );
    tempDir = File.createTempDirectory();

    var now:Date = new Date();
    var filename:String = "IMG" + now.fullYear + now.month + now.day + now.hours + now.minutes + now.seconds + ".jpg";
    var temp:File = tempDir.resolvePath( filename );

    var stream:FileStream = new FileStream();
    stream.open( temp, FileMode.WRITE );
    stream.writeBytes( imageBytes );
    stream.close();
         
    temp.addEventListener( Event.COMPLETE, uploadComplete );
    temp.addEventListener( IOErrorEvent.IO_ERROR, ioError );

    try
    {
        temp.upload( new URLRequest( serverURL ) );
    }
    catch( e:Error )
    {
        trace( e );
        removeTempDir();
        cameraUI.launch( MediaType.IMAGE )
    }
}

private function removeTempDir():void
{
    tempDir.deleteDirectory( true );
    tempDir = null;
}

您可以使用ActionScript 3开发人员指南中提供的这个PHP脚本示例测试上传功能。(请记住增大脚本中定义的最大文件大小,以容纳您的设备照相机生成的图像。)

URLLoader

如果能够控制接受图像上传的服务器端脚本,可以直接上传图像字节,而无需创建临时文件。在iOS上,这可以节省一定的内存,因为内存中的图像数据副本可能比其他时候更少。但与任何优化一样,您应该评估是否值得为预期的收益付出努力。

下面的函数以字节数组形式上传图像:

public function upload( data:ByteArray, destination:String, fileName:String = null ):void
{
    if( fileName == null ) //Make a name with correct file type
    {                
        var type:String = sniffFileType( buffer );
        var now:Date = new Date();
        fileName = "IMG" + now.fullYear + now.month +now.day +
            now.hours + now.minutes + now.seconds + ".jpg";
    }
            
    loader = new URLLoader();
    loader.dataFormat= URLLoaderDataFormat.BINARY;
            
    var urlString:String = destination + "?file=" + fileName;
    var request:URLRequest = new URLRequest( urlString );
            
    request.data = data;
    request.method = URLRequestMethod.POST;
    request.contentType = "application/octet-stream";
    loader.addEventListener( Event.COMPLETE, onUploadComplete );
    loader.addEventListener(IOErrorEvent.IO_ERROR, onUploadError );
    loader.load(request);           
} 

您可以在示例文件中的Uploader类中找到此函数的代码。您可以使用以下简单的PHP脚本测试该函数:

<?php
if ( isset ( $GLOBALS["HTTP_RAW_POST_DATA"] )) {
    $flux = $GLOBALS["HTTP_RAW_POST_DATA"];
    $fp = fopen('.images/' . $_GET['file'], 'wb');
    fwrite($fp, $flux);
    fclose($fp);
}
?>

延伸阅读

本文介绍了如何从媒体承诺对象获取图像数据,以及两种将该数据上传到服务器的轻松方式。广泛来讲,您的上传函数将需要添加标头和参数,才符合特定的照片服务API。也可以在ActionScript 3开发人员指南和ActionScript 3参考指南中找到关于CameraRoll和CameraUI的更多信息:

clip_image002+clip_image003

此作品依据Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License授权。超过本许可的与本作品中包含的代码示例相关的权限可在Adobe上找到。

查看原文:Uploading images from CameraRoll and CameraUI

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我
社区评论

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT