This example shows how to upload files in an Adobe AIR application to a server using Zend AMF and Flex. It’s based off a blog post by Leonardo França. I found getting started with Zend AMF a bit confusing, this tutorial by Ryan Stewart was very helpful.
On the server we need to upload the Zend Framework (minimal) and create some files. This is how the file structure will end up looking:

Create index.php (the gateway):
<?php
error_reporting(E_ALL|E_STRICT);
ini_set("display_errors", "on");
require('Zend/Amf/Server.php');
require_once('vo/FileVO.php');
require_once('service/UploadService.php');
$server = new Zend_Amf_Server();
$server->setClass("UploadService");
$server->setClassMap("FileVO","FileVO");
echo($server->handle());
?>
In the service directory create UploadService.php:
<?php
require_once($_SERVER['DOCUMENT_ROOT'].'/chaosm/air/ex2_uploadamf/vo/FileVO.php');
class UploadService
{
public function __construct() {}
public function upload(FileVO $data)
{
try
{
$fileData = $data->fileData;
file_put_contents($_SERVER['DOCUMENT_ROOT'].'/chaosm/air/ex2_uploadamf/uploads/'.$data->fileName,$fileData);
return true;
}
catch (Exception $e)
{
throw new Exception($e->getMessage());
}
}
}
?>
In the vo directory create FileVO.php (the value object for the file):
<?php
class FileVO
{
public $fileName;
public $fileData;
function __construct() {}
}
?>
In services-config.xml change the endpoint uri (line 17) to the location of the gateway on your server:
<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<services>
<service id="amfphp-flashremoting-service" class="flex.messaging.services.RemotingService" messageTypes="flex.messaging.messages.RemotingMessage">
<destination id="zendamf">
<channels>
<channel ref="zend-amf-channel"/>
</channels>
<properties>
<source>*</source>
</properties>
</destination>
</service>
</services>
<channels>
<channel-definition id="zend-amf-channel" class="mx.messaging.channels.AMFChannel">
<endpoint uri="http://www.chaosm.net/air/ex2_uploadamf/" class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>
</channels>
</services-config>
Finally make a directory called uploads. This is where files will be written to, so it needs to have the required permissions (such as 777).
In Flash Builder create a new Flex project called UploadAMF (or import my .fxp file). This is how the file structure will end up looking:

Right-click on the project and select Properties. Include the services-config.xml under additional compiler arguments like this:
Right-click on the src folder and create a new package called vo. In the vo package create a new ActionScript class called FileVO (notice that it has the same properties as the php value object on the server):
package vo
{
import flash.utils.ByteArray;
[RemoteClass(alias="FileVO")]
[Bindable]
public class FileVO
{
public var fileName:String;
public var fileData:ByteArray;
}
}
The main application file UploadAMF.mxml is where everything happens. A remote object is created, the user selects an image to upload and the upload method is called. When the upload is complete an Alert box is shown to the user.
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.FlexEvent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import vo.FileVO;
private var fileRef:FileReference;
private var fileTypes:FileFilter = new FileFilter("Images (*.jpg, *.jpeg)", "*.jpg; *.jpeg");
private var allTypes:Array = new Array(fileTypes)
protected function selectButton_clickHandler(event:MouseEvent):void
{
fileRef = new FileReference();
fileRef.addEventListener(Event.SELECT,selectHandler);
fileRef.browse(allTypes);
}
protected function selectHandler(event:Event):void
{
fileTextInput.text = fileRef.name;
fileRef.load();
}
protected function uploadButton_clickHandler(event:MouseEvent):void
{
var data:ByteArray = new ByteArray();
fileRef.data.readBytes(data,0,fileRef.data.length);
var fileVO:FileVO = new FileVO();
fileVO.fileName = fileRef.name;
fileVO.fileData = data;
upload.token = ro.upload(fileVO);
selectButton.enabled = false;
uploadButton.enabled = false;
}
protected function upload_faultHandler(event:FaultEvent):void
{
Alert.show(event.fault.faultString,"Error");
selectButton.enabled = true;
uploadButton.enabled = true;
}
protected function upload_resultHandler(event:ResultEvent):void
{
if (Boolean(event.message.body))
{
Alert.show("File uploaded.","Success");
}
else
{
Alert.show("Unable to upload file.","Error");
}
selectButton.enabled = true;
uploadButton.enabled = true;
}
]]>
</fx:Script>
<fx:Declarations>
<s:RemoteObject id="ro" destination="zendamf" source="UploadService"/>
<s:CallResponder id="upload" fault="upload_faultHandler(event)"
result="upload_resultHandler(event)"/>
</fx:Declarations>
<s:Form x="10" y="10">
<s:FormItem label="File">
<s:HGroup>
<s:TextInput id="fileTextInput"/>
<s:Button id="selectButton" label="Select" click="selectButton_clickHandler(event)"/>
<s:Button id="uploadButton" label="Upload" click="uploadButton_clickHandler(event)"/>
</s:HGroup>
</s:FormItem>
</s:Form>
</s:WindowedApplication>
Download the source files.
