This is a revised version of this post which allowed a user to upload a single file. Instead a user can select multiple files to upload.
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 images to upload and the upload method is called on each file. The FileReferenceList class provides the means to select multiple files for uploading.
<?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 fileRefList:FileReferenceList;
private var fileTypes:FileFilter = new FileFilter("Images (*.jpg, *.jpeg)", "*.jpg; *.jpeg");
private var allTypes:Array = new Array(fileTypes);
private var totalFilesCount:int = 0;
private var currentFileCount:int = 0;
protected function selectButton_clickHandler(event:MouseEvent):void
{
fileRefList = new FileReferenceList();
fileRefList.addEventListener(Event.SELECT,selectHandler);
fileRefList.browse(allTypes);
}
protected function selectHandler(event:Event):void
{
totalFilesCount = fileRefList.fileList.length;
currentFileCount = 0;
}
protected function uploadButton_clickHandler(event:MouseEvent):void
{
if (currentFileCount < totalFilesCount) {
uploadFile();
selectButton.enabled = false;
uploadButton.enabled = false;
}
}
public function uploadFile():void
{
if (currentFileCount < totalFilesCount) {
trace("current file = "+currentFileCount);
var currentFileRef:FileReference = new FileReference;
currentFileRef = fileRefList.fileList[currentFileCount];
currentFileRef.addEventListener(Event.COMPLETE,function fileLoadComplete(event:Event):void {
trace("file loaded into memory");
var data:ByteArray = new ByteArray();
currentFileRef.data.readBytes(data,0,currentFileRef.data.length);
var fileVO:FileVO = new FileVO();
fileVO.fileName = currentFileRef.name;
fileVO.fileData = data;
upload.token = ro.upload(fileVO);
});
currentFileRef.load();
} else {
trace("uploads complete");
selectButton.enabled = true;
uploadButton.enabled = true;
}
currentFileCount++;
}
protected function upload_faultHandler(event:FaultEvent):void
{
Alert.show(event.fault.faultString,"Error");
}
protected function upload_resultHandler(event:ResultEvent):void
{
if (Boolean(event.message.body))
{
trace("file uploaded");
}
else
{
trace("unable to upload file");
}
uploadFile();
}
]]>
</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="Upload Files">
<s:HGroup>
<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.
