Categories
Articles

Adobe AIR Native Drag and Drop File Upload With ProgressBar and PHP

This is an example of using Adobe AIR native drag and drop to upload a file to a web server using PHP5. The application only uploads one file at a time and assumes the drag operation is anywhere over the application.

This is the PHP code that will place the file into a folder named upload. The upload folder is a child folder under the folder for the PHP script. On line 13 you need to replace YOUR APPFOLDER with the folder you plan to install the PHP script. This also must be matched in the AIR MXML application.

You also need to set permissions on the upload folder to allow uploading.

The PHP script returns XML.The result.status node provide a true or false value for the success feedback.
file_upload.php

.
.
.
<?php
	$returnXML = "";
	$returnLog = "Receiving upload...\n";
    // Filedata is the default name used in uploading
	$returnLog .= "temporary file name = " . $_FILES['Filedata']['tmp_name']."\n";
	$returnLog .= "file name = " . $_FILES['Filedata']['name']."\n";
	$returnLog .= "file size = " . $_FILES['Filedata']['size']."\n";
	$file_temp = $_FILES['Filedata']['tmp_name'];
	$file_name = $_FILES['Filedata']['name'];
	$file_path = $_SERVER['DOCUMENT_ROOT']."/YOUR APPFOLDER/upload";
	$returnStatus = "false";
	$returnLog .= "attempting to move file...\n";
	if(  move_uploaded_file( $file_temp, $file_path . "/" . $file_name) )
	{
		$returnStatus = "true";
	}

	$returnLog .= "file move results = " . $returnStatus . "\n";
	$returnXML .= "<return>";
	$returnXML .= "<log>";
	$returnXML .= $returnLog;
	$returnXML .= "</log>";
	$returnXML .= "<status>";
	$returnXML .= $returnStatus;
	$returnXML .= "</status>";

	$returnXML .= "</return>";
	echo $returnXML ;

?>

This is the AIR main MXML code. You need to modify line 21 to include the URL to where you want to place the PHP script to handle the file upload.

.
.
.
<?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"
					   creationComplete="creationCompleteHandler(event)"
					   >
	<fx:Script>
		<![CDATA[
			import flash.desktop.ClipboardFormats;
			import flash.desktop.NativeDragManager;
			import flash.events.DataEvent;
			import flash.events.NativeDragEvent;
			import flash.filesystem.File;

			import mx.controls.Alert;
			import mx.events.FlexEvent;

			private const UPLOAD_SCRIPT_URL:String = "http://www.YOURDOMAIN.com/YOUR APPFOLDER/file_upload.php"

			protected function creationCompleteHandler(event:FlexEvent):void
			{
				console(className + ".creationCompleteHandler(...) - url:" + url );
				addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onDragEnterHandler);
				addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onDragDropHandler);
			}
			/**
			 * Event handler for NativeDragEvent.NATIVE_DRAG_ENTER.
			 * May be called multiple times as file object is dragged over the application.
			 * <p>
			 * Check that there are file objects being dragged and only one file is being dragged.
			 * </p>
			 * */
			private function onDragEnterHandler(e:NativeDragEvent):void
			{
				console(className + ".onDragIn(...)");
				//Does the clipboard have an array of File objects (AIR only)
				if(e.clipboard.hasFormat(ClipboardFormats.FILE_LIST_FORMAT))
				{
					//Get the array of File objects
					var files:Array = e.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;

					//Allow only one file as only supporting one in this application
					if(files.length == 1)
					{
						//Triggers NativeDragEvent.NATIVE_DRAG_DROP event.
						NativeDragManager.acceptDragDrop(this);
					}
				}
			}
			/**
			 * Event handler for NativeDragEvent.NATIVE_DRAG_DROP
			 * Occurs when the file object is dropped over the application
			 * */
			private function onDragDropHandler(e:NativeDragEvent):void
			{
				var urlRequest:URLRequest = new URLRequest();
				urlRequest.url = UPLOAD_SCRIPT_URL + "?cache=" + new Date().getTime();
				console(className + ".onDragDropHandler(...) - urlRequest.url:" + urlRequest.url);
				//Get the array of File objects. The array should only have one entry per onDragEnterHandler(...)
				var files:Array = e.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;

				//Create a File object and register events.
				var file:File = File(files[0]);
				file.addEventListener(Event.COMPLETE, file_CompleteHandler);
				file.addEventListener(Event.OPEN, file_OpenHandler);
				file.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, file_UploadCompleteDataHandler);
				file.addEventListener(IOErrorEvent.IO_ERROR, file_IOErrorHandler);
				file.addEventListener(HTTPStatusEvent.HTTP_STATUS , file_HTTPStatusHandler);
				file.addEventListener(SecurityErrorEvent.SECURITY_ERROR, file_SecurityErrorHandler);
				file.addEventListener(ProgressEvent.PROGRESS, file_progressHandler);

				console(className + ".onDragDropHandler(...) - file.url:" + file.url);
				console(className + ".onDragDropHandler(...) - file.name:" + file.name);
				console(className + ".onDragDropHandler(...) - file.nativePath:" + file.nativePath);

				try {
					// Start the file upload
					console(className + ".onDragDropHandler(...) - uploading " + file.name + " ...\n" );
					file.upload(urlRequest, "Filedata", false);
				}
				catch (error:Error) {
					console(className + ".onDragDropHandler(...) - Unable to upload file." + file.name);
					console(className + ".onDragDropHandler(...) - error." + error.toString());
				}
			}
			private function file_progressHandler(event:ProgressEvent):void {
				var file:FileReference = FileReference(event.target);
				console(className + ".progressHandler(...) - name=" + file.name + " bytesLoaded=" + event.bytesLoaded + " bytesTotal=" + event.bytesTotal);
				progressBar.setProgress(event.bytesLoaded, event.bytesTotal);
				progressBar.label = "CurrentProgress: " + Math.round( (event.bytesLoaded /event.bytesTotal * 100)) + "%";
			}

			/**
			 * Dispatched when an upload or download operation starts.
			 */
			private function file_OpenHandler(event:Event):void
			{
				console(className + ".file_OpenHandler(...) - event:" + event );
			}
			/**
			 * Dispatched when an upload fails and an HTTP status code is available to describe the failure.
			 */
			private function file_HTTPStatusHandler(event:HTTPStatusEvent):void
			{
				console(className + ".file_HTTPStatusHandler(...) - event:" + event );
			}
			/**
			 * Dispatched when the upload fails for various I/O reasons.
			 */

			private function file_IOErrorHandler(event:IOErrorEvent):void
			{
				console(className + ".file_IOErrorHandler(...) - event:" + event );
			}
			/**
			 * Dispatched when an operation violates a security constraint.
			 */

			private function file_SecurityErrorHandler(event:SecurityErrorEvent):void
			{
				console(className + ".file_SecurityErrorHandler(...) - event:" + event );
			}

			/**
			 * Dispatched when download is complete or when upload generates an HTTP status code of 200.
			 */
			public function file_CompleteHandler(event:Event):void
			{
				console(className + ".file_CompleteHandler(...) file uploaded complete");
			}

			/**
			 * Dispatched after data is received from the server after a successful upload.
			 */
			public function file_UploadCompleteDataHandler(event:DataEvent):void
			{
				var result:XML = new XML(event.data);
				console(className + ".file_UploadCompleteDataHandler(...) - STATUS:" + result.status );
				console(className + ".file_UploadCompleteDataHandler(...) - LOG:\n" + result.log );

			}

			/**
			 * Update the UI trace log
			 */
			private function console(msg:String):void
			{
				trace(msg);
				console_ta.appendText(msg + "\n");
			}

		]]>
	</fx:Script>
	<s:layout>
		<s:VerticalLayout paddingLeft="10" paddingTop="10"/>
	</s:layout>
	<s:Label  text="Adobe AIR Drag and Drop File Upload" fontSize="20" fontWeight="bold"/>
	<s:Line yFrom="10" yTo = "100" xFrom="0" xTo="0" width="100%" height="0"  >
		<!-- Define the border color of the line. -->
		<s:stroke>
			<s:SolidColorStroke color="0x000000" weight="1" caps="square"/>
		</s:stroke>
	</s:Line>

	<mx:ProgressBar id="progressBar" labelPlacement="bottom" minimum="0" visible="true" maximum="100"
					color="0x323232" label="CurrentProgress 0%" direction="right" mode="manual" width="100%"/>

	<s:TextArea  width="100%" height="100%" id="console_ta" fontFamily="_typewriter" lineBreak="explicit"/>
	<s:HGroup horizontalAlign="center" width="100%">
		<s:Button   label="Clear" click="{console_ta.text = '';}"/>
	</s:HGroup>
</s:WindowedApplication>

The AIR application and the PHP script do not check for maximum upload file size allowed. A typical upload limit number imposed by web hosting is the default 2 megabytes. You may be able to increase that number in the php.ini file or the .htaccess file. The latter is the alternative is you have no control over the php.ini file and the code is included here to bump the size up. In any case you are at the mercy of your hosting provider.

.htaccess

php_value upload_max_filesize 10M
php_value post_max_size 10M
php_value memory_limit 128M

In the AIR application you can create a FILE object in the onDragEnterHandler in the same way as the onDragDropHandler and use the file.size parameter for a check. For the PHP script you can use the $_FILES[‘Filedata’][‘size’] value.