Accessibility

Adobe AIR article

 

Uploading files to a server from an Adobe AIR application


Table of Contents

The upload process at the server

Nearly any server-side programming language can be used to handle file uploads at the server. ColdFusion, C#, PHP, PERL, and Java all have capabilities to handle file uploads in a relatively small amount of code.

From a web server–handling perspective, the method by which files are sent to the server involve a standard HTTP POST call to the supplied URL. Variables can be passed along with the HTTP POST call as well. The variables can be retrieved at the server using the same methods used to obtain variables in normal HTTP POST transactions.

Obtaining the actual file data is a little more involved and the exact methods will be dependent on the programming environment you are using. The data will be housed in a variable that, by default, will be named Filedata. You can override this, however, in ActionScript 3.0 by passing an additional optional parameter to the upload method.

For example, in the previous sample code showing a call to upload, it could have been called as:

thisFileRef.upload(uploadURL,"myData");

In this case, the file data will be housed in an HTTP POST variable named "myData".

A full analysis of how to retrieve the file data at the server from the variable that houses the file data is beyond the scope of this article. In the PERL programming language, for example, it is accomplished in only a few lines of code:

# $dataFile is the HTTP POST variable that houses the uploaded file data
my($buffer,$fileData);
while(my $bytesRead = read($dataFile,$buffer,1024)) {
   $fileData.=$buffer;
}
# $fileData now contains the full uploaded file data

Uploading files without the browse interface

There may be occasions when you want to give users the ability to upload files from their computers to a remote server without their needing to navigate the operating system file browse dialog box.

For example, the Flex Builder 3 Adobe AIR components provide a number of familiar UIComponents designed to provide file system navigation interfaces. Being able to drag and drop a file from a FileSystemTree component to an application feature that may represent a remote file share represents functionality you cannot achieve in an HTML-based application without the use of the operating system browse interface.

Uploading files without the browse interface

Figure 1. Uploading files without the browse interface

Because the File class in ActionScript 3.0 is a subclass of the FileReference class, all the methods and properties available to FileReference are available to File, including the most important one, upload.

The following application provides a simple example of similar functionality. In the example, the user selects a file from a FileSystemTree. The selected file is, after confirmation, uploaded to a remote server.

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
   <mx:Script>
       <![CDATA[
           import mx.events.CloseEvent;
           import flash.filesystem.File;
           import mx.controls.Alert;
           [Bindable]
           private var directory:File = new File(File.desktopDirectory.nativePath);
           private var selectedFile:File;
           private function confirmUpload(event:Event):void {
               selectedFile = new File(event.currentTarget.selectedItem.nativePath);
               Alert.show("Are you sure you want to upload "+selectedFile.nativePath+"?","Confirm",Alert.YES|Alert.NO,null,doUpload);
           }
           private function doUpload(event:CloseEvent):void {
               if(event.detail==1) {
                   selectedFile.upload(new URLRequest("http://www.o2apps.com/fileUpload.pl");
               }
           }
       ]]>
   </mx:Script>
   <mx:FileSystemTree x="10" y="28" width="280" height="308" directory="{directory}"dragEnabled="true" change="confirmUpload(event);"/>
   <mx:Text x="10" y="2" text="Select the file you want to upload"/>
</mx:WindowedApplication>

The File class essentially enables you to create a reference to a file located anywhere on the computer and then upload that file to a remote server.

Obviously, this is the source of terrific security implications and it is expected that during the installation process, the end user will be given the ability to determine what local directories the Adobe AIR application they are installing should have access.

Uploading files without a File or FileReference Object

Suppose you have data you want to upload as a file but the data is stored as a data structure in your application at runtime and not on the local file system. This might be the case, for example, in a photo upload application where the user captures a photo of him- or herself from a desktop camera and then the application uploads that photo to a remote application server—without that photo ever residing on the computer as a file.

Below is the MXML application that references the custom class classes.displays.CameraStream. The ActionScript code found below the MXML application must be placed in a path (relative to the application) of classes/displays/CameraStream.as.

The CameraStream class abstracts the business of creating the BitMap data from the camera and a full exploration of this class is beyond the scope of this article.

The relevant function for this article is the uploadPhoto function in the below MXML application. Refer to the function's comments for an explanation of the techniques employed.

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="init();" width="778" height="495">
   <mx:Script>
       <![CDATA[
           import mx.graphics.codec.JPEGEncoder;
           import flash.media.Camera;
           import classes.displays.CameraStream;
           import mx.core.UIComponent;
           private var cameraStream:CameraStream;
           private function init():void {
               cameraStream = new CameraStream(320,240);
               var ref:UIComponent = new UIComponent();
               preview.removeAllChildren();
               preview.addChild(ref);
               ref.addChild( cameraStream );
           }
           private function takeSnapshot():void {
               imageViewer.visible = true;
               imageViewer.width = preview.width;
               imageViewer.height = preview.height;
               var uiComponent : UIComponent = new UIComponent();
               uiComponent.width = cameraStream.width;
               uiComponent.height = cameraStream.height;
               var photoData:Bitmap = cameraStream.getSnapshot();
               var photoBitmap:BitmapData = photoData.bitmapData;
               uiComponent.addChild(photoData);
               imageViewer.removeAllChildren();
               imageViewer.addChild(uiComponent);
               var jpgEncoder:JPEGEncoder = new JPEGEncoder();
               var jpgBytes:ByteArray = jpgEncoder.encode(photoBitmap);
               uploadPhoto(jpgBytes);
           }
           private function uploadPhoto(imageData:ByteArray):void {
               // method accepts raw JPEG data as ByteArray as input
               // create a URLRequest object pointed at the URL of the remote upload script
               var request:URLRequest = new URLRequest("http://www.o2apps.com/fileUpload.pl";
               // the call we will make is a standard HTTP POST call
               request.method = "POST";
               // this enables us to send binary data for the body of the HTTP POST call
               request.contentType = "application/octet-stream";
               var loader:URLLoader = new URLLoader();               
               loader.addEventListener(Event.COMPLETE,uploadPhotoHandler);
               // the loader is the data being sent along to the server. Its dataFormat property lets us specify the format for the body, which, in our case, will be BINARY data
               loader.dataFormat = URLLoaderDataFormat.BINARY;
               // the data property of our URLRequest object is the actual data being sent to the server, which in this case is the photo JPEG data
               request.data = imageData;
               loader.load(request);
           }
           private function uploadPhotoHandler(event:Event):void {
               trace("Response from server: "+event.target.data);
           }
       ]]>
   </mx:Script>
   <mx:Panel x="25" y="10" width="340" height="280" id="preview" title="Photo Maker"/>
   <mx:Panel x="406" y="10"width="340" height="280" layout="absolute" title="Preview" id="imageViewer"/>
   <mx:Button x="25" y="298" label="Upload Photo" id="uploadPhotoButton" click="takeSnapshot();"/>
</mx:WindowedApplication>
package classes.displays {
   import flash.media.Camera;
   import flash.media.Video;
   import flash.events.*;
   import flash.display.Sprite;
   import flash.display.BitmapData;
   import flash.geom.Matrix;
   import flash.display.Bitmap;
   import flash.system.Security;
   import flash.system.SecurityPanel;
   public class CameraStream extends Sprite {
       public static const DEFAULT_CAMERA_WIDTH : Number = 320;
       public static const DEFAULT_CAMERA_HEIGHT : Number = 240;
       public static const DEFAULT_CAMERA_FPS : Number = 30;
       public var video:Video;
       private var camera:Camera;
       private var _cameraWidth : Number;
       private var _cameraHeight : Number;
       public function CameraStream(w:Number, h:Number) {
           camera = Camera.getCamera();
           _cameraWidth = w; // DEFAULT_CAMERA_WIDTH;
           _cameraHeight = h; // DEFAULT_CAMERA_HEIGHT;
           if(camera != null) {
               camera.setMode(_cameraWidth, _cameraHeight, DEFAULT_CAMERA_FPS)
               video = new Video(camera.width, camera.height);
               video.attachCamera(camera);
               addChild(video); 
           }
           else {
               Security.showSettings(SecurityPanel.CAMERA)
           }
       }
       public function getSnapshotBitmapData():BitmapData {
           var snapshot:BitmapData = new BitmapData(_cameraWidth, _cameraHeight);
           snapshot.draw(video,new Matrix());
           return snapshot;
       }
       public function getSnapshot():Bitmap {
           var bitmap : Bitmap = new Bitmap(getSnapshotBitmapData());
           return bitmap;
       }
   }
} 

Retrieving uploaded data at the server

The exact technique leveraged at the server to capture the uploaded data will depend on the programming language employed. The code employed, using the PERL programming language, would appear as follows:

#!/usr/local/bin/perl

use CGI;
my $query = new CGI;
my %fileObject = %{$query};
my @postData = @{$thisQuery{"POSTDATA"}};

my $fileData;
foreach my $this (@postData) {
   $fileData.=$this;
}

# $fileData now contains the contents of the uploaded file

The above technique could be similarly accomplished using PHP, ColdFusion, C#, Java, or any other programming language.

Where to go from here

Uploading files from an Adobe AIR application to a remote content management system or web server has the potential to change the landscape for collaborative publishing tools. Adobe AIR, through ActionScript 3.0, provides a number of easy and best-practice means to accomplish getting files from the user's computer to your application server for sharing and application-specific processing, thus enriching the user's overall experience when using your application.

For more information about Adobe AIR, visit the product page.

For more inspiration, check out the sample applications in the Adobe AIR Developer Center for Flex, Flash, and HTML/Ajax, as well as the Adobe AIR Developer Derby winners and the apps showcase.

To get started building Flex apps on AIR go to the Getting Started section of the Adobe AIR Developer Center for Flex or dig into Developing Adobe AIR applications with Adobe Flex. To dive right in and begin building AIR applications in Flex Builder, follow the simple steps in Developing AIR application with Flex on Adobe LiveDocs or explore popular Adobe AIR APIs by working with the AIR Quick Starts.