WhenWhatHowLong - A Simple Time and Task Tracker

AIR, WhenWhatHowLong

Over the weekend, out of necessity, I threw together a very simple AIR app for tracking tasks, when they were done and how long they took.  It's basically a glorified stopwatch with historical logging.  If this is something that interests you I encourage you to check it out and leave me some feedback.

Enjoy! slantsoft.com/index.cfm/products/wwhl

+1

Moving Images Between CF 8 and Flex 3 (AIR 1.5) - Part 2 of 2

Coldfusion, Flex, cfimage, AIR

In my previous post I covered the ColdFusion 8 half of moving image data between CF8 and Flex 3, AIR 1.5 in this example.  This post will be a bit longer since there is a bit more ActionScript (and MXML) than there was CFML.  I am going to assume that you know how to setup a new AIR / Flex project as that falls out of the scope of this topic.

I started with a pretty basic layout for my application.  I have a ComboBox that displays the names of the available images, a button to browse and select a new image to add to the server, a TextInput to display the name of the selected image, an upload button to set the image data to the server and a delete button, obviously, to delete the selected image.  Below all that I have a canvas with it’s height and width set to 100% which I will use to contain and display the image I’ve selected.

<mx:HBox width="100%">
	<mx:ComboBox id="imageSelect" prompt="Select an image..." dataProvider="{_imageArray}" change="onImageSelect()"/>
	<mx:Button label="Add Image (Preview)" click="selectImage()"/>
	<mx:TextInput id="imageNameTI" text="{this.previewFileName}" enabled="false"/>
	<mx:Button id="uploadBtn" label="Upload" enabled="false" click="uploadImage()"/>
	<mx:Button id="deleteBtn" label="Delete Selected Image" enabled="false" click="deleteSelectedImage()"/>
</mx:HBox>


Next I’ve defined my RemoteObject and the methods I want to call.  Note that since I am working with AIR I need to specify an endpoint for my RemoteObject, in my case http://localhost:8302/flex2gateway .  Without the endpoint the RemoteObject will not know where your CFC lives.  Also, note the ‘/flex2gateway’ at the end of my url.  This is how the RemoteObject talks to CF and it is required when connecting an Adobe ColdFusion server, I can’t speak for the other projects like OpenBD or Railo since I haven’t used remoting with them.

<mx:RemoteObject id="RO" endpoint="http://localhost:8302/flex2gateway" destination="ColdFusion"
	source="ImageService" fault="onROFault(event)">
	
	<mx:method name="getImageArray" result="onGetImageArrayResult(event)"/>
	<mx:method name="getImage" result="onGetImageResult(event)"/>
	<mx:method name="setImage" result="this.RO.getImageArray()"/>
	<mx:method name="deleteImage" result="this.RO.getImageArray()"/>
	
</mx:RemoteObject>


Each of the methods in my RemoteObject correspond to it’s counterpart in the CFC.  In both the getImageArray and getImage methods I am calling an actionscript method and passing the ResultEvent as an argument.  Rather than calling a method when a result is returned from the setImage and deleteImage methods I am directly invoking the getImageArray method on the RemoteObject, this will just update my list of available images in the ComboBox.


ActionScript methods.

The init() method is called when the creationComplete event is dispatched by the application.  In it I am making a call to the getImageArray method.

private function init():void{
	this.RO.getImageArray();
}


The onROFault() method handles any fault events from the RO RemoteObject.  I’m just popping up an Alert with the fault strings and clearing the busy cursor if we have one.

private function onROFault(event:FaultEvent):void{
	Alert.show(event.fault.faultString,event.fault.faultDetail);
	cursorManager.removeBusyCursor();
}


The onGetImageArray() method is called when a result is returned from the getImageArray() method.  I am casting the event.result into a bindable array that I’ve defined at the top of my script block.  The array, _imageArray, is set as the dataProvider for my ComboBox, and because it is bindable any changes made to _imageArray will be reflected in the contents of the ComboBox’s drop down.  I am also removing the busy cursor if we are displaying one.

private function onGetImageArrayResult(event:ResultEvent):void{
	_imageArray = event.result as Array;
	cursorManager.removeBusyCursor();
}


The onImageSelect() method is called when an image name is selected from the ComboBox.  I’m calling another method, clearPreview(), to sort of reset the application, more on this later.  Next I call the getImage() method passing the selected image name from the ComboBox as an argument and showing the busy cursor.

private function onImageSelect():void{
	clearPreview();
	
	this.RO.getImage(this.imageSelect.selectedItem);
	this.cursorManager.setBusyCursor();
}


The onGetImageResult() method is the handler called by the result event from calling getImage().  Here’s where we start to see how we handle the image data returned from ColdFusion.

private function onGetImageResult(event:ResultEvent):void{
	var tempImg:Image = new Image;
	tempImg.height = event.result.Height;
	tempImg.width = event.result.Width;
	tempImg.data = event.result.Data;
	this.imgCanvas.removeAllChildren();
	this.imgCanvas.addChild(tempImg);
	cursorManager.removeBusyCursor();
	this.deleteBtn.enabled = true;
}


First I create a new Image and set it’s height and width to the returned Height and Width values.

var tempImg:Image = new Image;
tempImg.height = event.result.Height;
tempImg.width = event.result.Width;


Next we set the data property of the Image equal to the returned binary blob.  This was the data we got when we used the ImageGetBlob() function in our CFC.  Binary data in ColdFusion is the same as a ByteArray in Flex, and since the data attribute of our image expects a ByteArray we can pass the returned value right in without any data casting or translation.  Pretty cool huh?

tempImg.data = event.result.Data;


After setting up the image I remove any children the canvas might have so that Flex doesn’t just stack the images one on top of the other.  Next I add the image as a child to the canvas, remove the busy cursor and enable my delete button.

this.imgCanvas.removeAllChildren();
this.imgCanvas.addChild(tempImg);
cursorManager.removeBusyCursor();
this.deleteBtn.enabled = true;


The selectImage() method is called when the “Add Image” button is clicked.  I create an array of image file types the application will accept, I defined the values in a variable at the top of the script block, all other file types will not be selectable in the file browser.  Next I open the native file browser, I set the default directory as the user’s documents directory at the top of the script block.  Then i add an event listener for when a file is selected.

private function selectImage():void{
	var imageTypesArray:Array = new Array(this.imageTypes);
	this.imageFile.browse(imageTypesArray);
	this.imageFile.addEventListener(Event.SELECT,imageSelected);
}


The imageSelected() method is called when a file has been chosen through the native file browser.  Here again I am using ByteArrays to store the image data.  I’m also using a FileStream to read the bytes from the selected image and store them in my ByteArray.

private function imageSelected(event:Event):void{
	this.ImageByteArray = new ByteArray();
	this.ImageFileStream = new FileStream();
	this.ImageFileStream.open(this.imageFile,FileMode.READ);
	this.ImageFileStream.readBytes(this.ImageByteArray);
	
	this.imgPreview = new Image;
	this.imgPreview.data = this.ImageByteArray;
	this.imgCanvas.removeAllChildren();
	this.imgCanvas.addChild(imgPreview);
	
	this.uploadBtn.enabled = true;
	this.imageNameTI.enabled = true;
	
	this.previewFileType = event.currentTarget.type;
	this.previewFileName = event.currentTarget.name.replace(this.previewFileType,'');
}


Next I create a new Image much like i did before, however, since I haven’t figured out how to read the image metadata with Flex / AIR I am letting Flex figure out the image’s dimensions based on just the image created by the ByteArray.  Then I remove any children from the canvas, giving me a clear work area in which to add my new image.

this.imgPreview = new Image;
this.imgPreview.data = this.ImageByteArray;
this.imgCanvas.removeAllChildren();
this.imgCanvas.addChild(imgPreview);


Next I enable my Upload button and the TextInput so the user can rename the image if they want.

this.uploadBtn.enabled = true;
this.imageNameTI.enabled = true;


Lastly I store the image format (extension) and strip the extension from the file name, giving the user the ability to only rename the file, but not change it’s format.  If you want to let your users change the format you can, it’s just a matter of giving them the options your server can read.

this.previewFileType = event.currentTarget.type;
this.previewFileName = event.currentTarget.name.replace(this.previewFileType,'');


At this point I haven’t actually sent the image data to the server, this is simply a preview mode so that the user can see the image selected.  From reading a few other blog posts it seems that this behavior should also be possible in Flash Player 10 apps, where the client side application is able to store the ByteArray in memory and manipulate it without having to interact with the server.  As I understand it, Flash Player 9 would require the data to be sent to the server then returned from the server in order to work with it.  Please correct me if my assumption is incorrect.

The uploadImage() method is called when the upload button is clicked.  In it I make a call to the setImage() method in my RemoteObject, passing the image ByteArray, image name and image format (extension) as arguments.  Then I call the clearPreview() method to ‘reset’ my application and show the busy cursor.

private function uploadImage():void{
	this.RO.setImage(this.ImageByteArray,this.imageNameTI.text,this.previewFileType);
	clearPreview();
	this.cursorManager.setBusyCursor();
}


The clearPreview() method removes all children from the canvas, disables the upload button and text input and sets the file name to null.

private function clearPreview():void{
	this.uploadBtn.enabled = false;
	this.imageNameTI.enabled = false;
	this.previewFileName = null;
	this.imgCanvas.removeAllChildren();
}


The deleteSelectedImage() method calls the deleteImage() in the RemoteObject, passing the selected image name as an argument.  Then it disables the delete button and removes all children from the canvas.

private function deleteSelectedImage():void{
	this.RO.deleteImage(this.imageSelect.selectedItem);
	this.deleteBtn.enabled = false;
	this.imgCanvas.removeAllChildren();
}


And here is the completed MXML file.

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" horizontalAlign="left" creationComplete="init()">
	<mx:Script>
		<![CDATA[
			import mx.controls.Image;
			import mx.rpc.events.ResultEvent;
			import mx.rpc.events.FaultEvent;
			import mx.controls.Alert;
			
			[Bindable] private var _imageArray:Array;
			private var imageFile:File = File.documentsDirectory;
			private var imageTypes:FileFilter = new FileFilter("Images (*.jpg; *.jpeg; *.gif; *.png)" ,"*.jpg; *.jpeg; *.gif; *.png");
			private var ImageByteArray:ByteArray;
			private var ImageFileStream:FileStream;
			private var imgPreview:Image;
			private var previewFileType:String;
			[Bindable] private var previewFileName:String;
			
			private function init():void{
				this.RO.getImageArray();
			}
			private function onROFault(event:FaultEvent):void{
				Alert.show(event.fault.faultString,event.fault.faultDetail);
				cursorManager.removeBusyCursor();
			}
			private function onGetImageArrayResult(event:ResultEvent):void{
				_imageArray = event.result as Array;
				cursorManager.removeBusyCursor();
			}
			private function onImageSelect():void{
				clearPreview();
				
				this.RO.getImage(this.imageSelect.selectedItem);
				this.cursorManager.setBusyCursor();
			}
			private function onGetImageResult(event:ResultEvent):void{
				var tempImg:Image = new Image;
				tempImg.height = event.result.Height;
				tempImg.width = event.result.Width;
				tempImg.data = event.result.Data;
				this.imgCanvas.removeAllChildren();
				this.imgCanvas.addChild(tempImg);
				cursorManager.removeBusyCursor();
				this.deleteBtn.enabled = true;
			}
			private function selectImage():void{
				var imageTypesArray:Array = new Array(this.imageTypes);
				this.imageFile.browse(imageTypesArray);
				this.imageFile.addEventListener(Event.SELECT,imageSelected);
			}
			private function imageSelected(event:Event):void{
				this.ImageByteArray = new ByteArray();
				this.ImageFileStream = new FileStream();
				this.ImageFileStream.open(this.imageFile,FileMode.READ);
				this.ImageFileStream.readBytes(this.ImageByteArray);
				
				this.imgPreview = new Image;
				this.imgPreview.data = this.ImageByteArray;
				this.imgCanvas.removeAllChildren();
				this.imgCanvas.addChild(imgPreview);
				
				this.uploadBtn.enabled = true;
				this.imageNameTI.enabled = true;
				
				this.previewFileType = event.currentTarget.type;
				this.previewFileName = event.currentTarget.name.replace(this.previewFileType,'');
			}
			private function uploadImage():void{
				this.RO.setImage(this.ImageByteArray,this.imageNameTI.text,this.previewFileType);
				clearPreview();
				this.cursorManager.setBusyCursor();
			}
			private function clearPreview():void{
				this.uploadBtn.enabled = false;
				this.imageNameTI.enabled = false;
				this.previewFileName = null;
				this.imgCanvas.removeAllChildren();
			}
			private function deleteSelectedImage():void{
				this.RO.deleteImage(this.imageSelect.selectedItem);
				this.deleteBtn.enabled = false;
				this.imgCanvas.removeAllChildren();
			}
		]]>
	</mx:Script>
	
	<mx:RemoteObject id="RO" endpoint="http://localhost:8302/flex2gateway" destination="ColdFusion"
		source="ImageService" fault="onROFault(event)">
		
		<mx:method name="getImageArray" result="onGetImageArrayResult(event)"/>
		<mx:method name="getImage" result="onGetImageResult(event)"/>
		<mx:method name="setImage" result="this.RO.getImageArray()"/>
		<mx:method name="deleteImage" result="this.RO.getImageArray()"/>
		
	</mx:RemoteObject>
	
	<mx:HBox width="100%">
		<mx:ComboBox id="imageSelect" prompt="Select an image..." dataProvider="{_imageArray}" change="onImageSelect()"/>
		<mx:Button label="Add Image (Preview)" click="selectImage()"/>
		<mx:TextInput id="imageNameTI" text="{this.previewFileName}" enabled="false"/>
		<mx:Button id="uploadBtn" label="Upload" enabled="false" click="uploadImage()"/>
		<mx:Button id="deleteBtn" label="Delete Selected Image" enabled="false" click="deleteSelectedImage()"/>
	</mx:HBox>
	
	<mx:Canvas id="imgCanvas" width="100%" height="100%"/>
	
</mx:WindowedApplication>


So there you have it, you can now move image data between CF8 and Flex 3 apps without having to make http post or get calls from the Flex app.  Delivering content via binary data / ByteArrays makes it possible to deliver content to your Flex applications without having to expose that content to the public web via a URL, combined with using RemoteObject over SSL you could make a pretty slick and secure application.

+1

Moving Images Between CF 8 and Flex 3 (AIR 1.5) - Part 1 of 2

Coldfusion, Flex, cfimage, AIR

In my previous post I covered the ColdFusion 8 half of moving image data between CF8 and Flex 3, AIR 1.5 in this example.  This post will be a bit longer since there is a bit more ActionScript (and MXML) than there was CFML.  I am going to assume that you know how to setup a new AIR / Flex project as that falls out of the scope of this topic.

I started with a pretty basic layout for my application.  I have a ComboBox that displays the names of the available images, a button to browse and select a new image to add to the server, a TextInput to display the name of the selected image, an upload button to set the image data to the server and a delete button, obviously, to delete the selected image.  Below all that I have a canvas with it’s height and width set to 100% which I will use to contain and display the image I’ve selected.

<mx:HBox width="100%">
	<mx:ComboBox id="imageSelect" prompt="Select an image..." dataProvider="{_imageArray}" change="onImageSelect()"/>
	<mx:Button label="Add Image (Preview)" click="selectImage()"/>
	<mx:TextInput id="imageNameTI" text="{this.previewFileName}" enabled="false"/>
	<mx:Button id="uploadBtn" label="Upload" enabled="false" click="uploadImage()"/>
	<mx:Button id="deleteBtn" label="Delete Selected Image" enabled="false" click="deleteSelectedImage()"/>
</mx:HBox>


Next I’ve defined my RemoteObject and the methods I want to call.  Note that since I am working with AIR I need to specify an endpoint for my RemoteObject, in my case http://localhost:8302/flex2gateway .  Without the endpoint the RemoteObject will not know where your CFC lives.  Also, note the ‘/flex2gateway’ at the end of my url.  This is how the RemoteObject talks to CF and it is required when connecting an Adobe ColdFusion server, I can’t speak for the other projects like OpenBD or Railo since I haven’t used remoting with them.

<mx:RemoteObject id="RO" endpoint="http://localhost:8302/flex2gateway" destination="ColdFusion"
	source="ImageService" fault="onROFault(event)">
	
	<mx:method name="getImageArray" result="onGetImageArrayResult(event)"/>
	<mx:method name="getImage" result="onGetImageResult(event)"/>
	<mx:method name="setImage" result="this.RO.getImageArray()"/>
	<mx:method name="deleteImage" result="this.RO.getImageArray()"/>
	
</mx:RemoteObject>


Each of the methods in my RemoteObject correspond to it’s counterpart in the CFC.  In both the getImageArray and getImage methods I am calling an actionscript method and passing the ResultEvent as an argument.  Rather than calling a method when a result is returned from the setImage and deleteImage methods I am directly invoking the getImageArray method on the RemoteObject, this will just update my list of available images in the ComboBox.


ActionScript methods.

The init() method is called when the creationComplete event is dispatched by the application.  In it I am making a call to the getImageArray method.

private function init():void{
	this.RO.getImageArray();
}


The onROFault() method handles any fault events from the RO RemoteObject.  I’m just popping up an Alert with the fault strings and clearing the busy cursor if we have one.

private function onROFault(event:FaultEvent):void{
	Alert.show(event.fault.faultString,event.fault.faultDetail);
	cursorManager.removeBusyCursor();
}


The onGetImageArray() method is called when a result is returned from the getImageArray() method.  I am casting the event.result into a bindable array that I’ve defined at the top of my script block.  The array, _imageArray, is set as the dataProvider for my ComboBox, and because it is bindable any changes made to _imageArray will be reflected in the contents of the ComboBox’s drop down.  I am also removing the busy cursor if we are displaying one.

private function onGetImageArrayResult(event:ResultEvent):void{
	_imageArray = event.result as Array;
	cursorManager.removeBusyCursor();
}


The onImageSelect() method is called when an image name is selected from the ComboBox.  I’m calling another method, clearPreview(), to sort of reset the application, more on this later.  Next I call the getImage() method passing the selected image name from the ComboBox as an argument and showing the busy cursor.

private function onImageSelect():void{
	clearPreview();
	
	this.RO.getImage(this.imageSelect.selectedItem);
	this.cursorManager.setBusyCursor();
}


The onGetImageResult() method is the handler called by the result event from calling getImage().  Here’s where we start to see how we handle the image data returned from ColdFusion.

private function onGetImageResult(event:ResultEvent):void{
	var tempImg:Image = new Image;
	tempImg.height = event.result.Height;
	tempImg.width = event.result.Width;
	tempImg.data = event.result.Data;
	this.imgCanvas.removeAllChildren();
	this.imgCanvas.addChild(tempImg);
	cursorManager.removeBusyCursor();
	this.deleteBtn.enabled = true;
}


First I create a new Image and set it’s height and width to the returned Height and Width values.

var tempImg:Image = new Image;
tempImg.height = event.result.Height;
tempImg.width = event.result.Width;


Next we set the data property of the Image equal to the returned binary blob.  This was the data we got when we used the ImageGetBlob() function in our CFC.  Binary data in ColdFusion is the same as a ByteArray in Flex, and since the data attribute of our image expects a ByteArray we can pass the returned value right in without any data casting or translation.  Pretty cool huh?

tempImg.data = event.result.Data;


After setting up the image I remove any children the canvas might have so that Flex doesn’t just stack the images one on top of the other.  Next I add the image as a child to the canvas, remove the busy cursor and enable my delete button.

this.imgCanvas.removeAllChildren();
this.imgCanvas.addChild(tempImg);
cursorManager.removeBusyCursor();
this.deleteBtn.enabled = true;


The selectImage() method is called when the “Add Image” button is clicked.  I create an array of image file types the application will accept, I defined the values in a variable at the top of the script block, all other file types will not be selectable in the file browser.  Next I open the native file browser, I set the default directory as the user’s documents directory at the top of the script block.  Then i add an event listener for when a file is selected.

private function selectImage():void{
	var imageTypesArray:Array = new Array(this.imageTypes);
	this.imageFile.browse(imageTypesArray);
	this.imageFile.addEventListener(Event.SELECT,imageSelected);
}


The imageSelected() method is called when a file has been chosen through the native file browser.  Here again I am using ByteArrays to store the image data.  I’m also using a FileStream to read the bytes from the selected image and store them in my ByteArray.

private function imageSelected(event:Event):void{
	this.ImageByteArray = new ByteArray();
	this.ImageFileStream = new FileStream();
	this.ImageFileStream.open(this.imageFile,FileMode.READ);
	this.ImageFileStream.readBytes(this.ImageByteArray);
	
	this.imgPreview = new Image;
	this.imgPreview.data = this.ImageByteArray;
	this.imgCanvas.removeAllChildren();
	this.imgCanvas.addChild(imgPreview);
	
	this.uploadBtn.enabled = true;
	this.imageNameTI.enabled = true;
	
	this.previewFileType = event.currentTarget.type;
	this.previewFileName = event.currentTarget.name.replace(this.previewFileType,'');
}


Next I create a new Image much like i did before, however, since I haven’t figured out how to read the image metadata with Flex / AIR I am letting Flex figure out the image’s dimensions based on just the image created by the ByteArray.  Then I remove any children from the canvas, giving me a clear work area in which to add my new image.

this.imgPreview = new Image;
this.imgPreview.data = this.ImageByteArray;
this.imgCanvas.removeAllChildren();
this.imgCanvas.addChild(imgPreview);


Next I enable my Upload button and the TextInput so the user can rename the image if they want.

this.uploadBtn.enabled = true;
this.imageNameTI.enabled = true;


Lastly I store the image format (extension) and strip the extension from the file name, giving the user the ability to only rename the file, but not change it’s format.  If you want to let your users change the format you can, it’s just a matter of giving them the options your server can read.

this.previewFileType = event.currentTarget.type;
this.previewFileName = event.currentTarget.name.replace(this.previewFileType,'');


At this point I haven’t actually sent the image data to the server, this is simply a preview mode so that the user can see the image selected.  From reading a few other blog posts it seems that this behavior should also be possible in Flash Player 10 apps, where the client side application is able to store the ByteArray in memory and manipulate it without having to interact with the server.  As I understand it, Flash Player 9 would require the data to be sent to the server then returned from the server in order to work with it.  Please correct me if my assumption is incorrect.

The uploadImage() method is called when the upload button is clicked.  In it I make a call to the setImage() method in my RemoteObject, passing the image ByteArray, image name and image format (extension) as arguments.  Then I call the clearPreview() method to ‘reset’ my application and show the busy cursor.

private function uploadImage():void{
	this.RO.setImage(this.ImageByteArray,this.imageNameTI.text,this.previewFileType);
	clearPreview();
	this.cursorManager.setBusyCursor();
}


The clearPreview() method removes all children from the canvas, disables the upload button and text input and sets the file name to null.

private function clearPreview():void{
	this.uploadBtn.enabled = false;
	this.imageNameTI.enabled = false;
	this.previewFileName = null;
	this.imgCanvas.removeAllChildren();
}


The deleteSelectedImage() method calls the deleteImage() in the RemoteObject, passing the selected image name as an argument.  Then it disables the delete button and removes all children from the canvas.

private function deleteSelectedImage():void{
	this.RO.deleteImage(this.imageSelect.selectedItem);
	this.deleteBtn.enabled = false;
	this.imgCanvas.removeAllChildren();
}


And here is the completed MXML file.

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" horizontalAlign="left" creationComplete="init()">
	<mx:Script>
		<![CDATA[
			import mx.controls.Image;
			import mx.rpc.events.ResultEvent;
			import mx.rpc.events.FaultEvent;
			import mx.controls.Alert;
			
			[Bindable] private var _imageArray:Array;
			private var imageFile:File = File.documentsDirectory;
			private var imageTypes:FileFilter = new FileFilter("Images (*.jpg; *.jpeg; *.gif; *.png)" ,"*.jpg; *.jpeg; *.gif; *.png");
			private var ImageByteArray:ByteArray;
			private var ImageFileStream:FileStream;
			private var imgPreview:Image;
			private var previewFileType:String;
			[Bindable] private var previewFileName:String;
			
			private function init():void{
				this.RO.getImageArray();
			}
			private function onROFault(event:FaultEvent):void{
				Alert.show(event.fault.faultString,event.fault.faultDetail);
				cursorManager.removeBusyCursor();
			}
			private function onGetImageArrayResult(event:ResultEvent):void{
				_imageArray = event.result as Array;
				cursorManager.removeBusyCursor();
			}
			private function onImageSelect():void{
				clearPreview();
				
				this.RO.getImage(this.imageSelect.selectedItem);
				this.cursorManager.setBusyCursor();
			}
			private function onGetImageResult(event:ResultEvent):void{
				var tempImg:Image = new Image;
				tempImg.height = event.result.Height;
				tempImg.width = event.result.Width;
				tempImg.data = event.result.Data;
				this.imgCanvas.removeAllChildren();
				this.imgCanvas.addChild(tempImg);
				cursorManager.removeBusyCursor();
				this.deleteBtn.enabled = true;
			}
			private function selectImage():void{
				var imageTypesArray:Array = new Array(this.imageTypes);
				this.imageFile.browse(imageTypesArray);
				this.imageFile.addEventListener(Event.SELECT,imageSelected);
			}
			private function imageSelected(event:Event):void{
				this.ImageByteArray = new ByteArray();
				this.ImageFileStream = new FileStream();
				this.ImageFileStream.open(this.imageFile,FileMode.READ);
				this.ImageFileStream.readBytes(this.ImageByteArray);
				
				this.imgPreview = new Image;
				this.imgPreview.data = this.ImageByteArray;
				this.imgCanvas.removeAllChildren();
				this.imgCanvas.addChild(imgPreview);
				
				this.uploadBtn.enabled = true;
				this.imageNameTI.enabled = true;
				
				this.previewFileType = event.currentTarget.type;
				this.previewFileName = event.currentTarget.name.replace(this.previewFileType,'');
			}
			private function uploadImage():void{
				this.RO.setImage(this.ImageByteArray,this.imageNameTI.text,this.previewFileType);
				clearPreview();
				this.cursorManager.setBusyCursor();
			}
			private function clearPreview():void{
				this.uploadBtn.enabled = false;
				this.imageNameTI.enabled = false;
				this.previewFileName = null;
				this.imgCanvas.removeAllChildren();
			}
			private function deleteSelectedImage():void{
				this.RO.deleteImage(this.imageSelect.selectedItem);
				this.deleteBtn.enabled = false;
				this.imgCanvas.removeAllChildren();
			}
		]]>
	</mx:Script>
	
	<mx:RemoteObject id="RO" endpoint="http://localhost:8302/flex2gateway" destination="ColdFusion"
		source="ImageService" fault="onROFault(event)">
		
		<mx:method name="getImageArray" result="onGetImageArrayResult(event)"/>
		<mx:method name="getImage" result="onGetImageResult(event)"/>
		<mx:method name="setImage" result="this.RO.getImageArray()"/>
		<mx:method name="deleteImage" result="this.RO.getImageArray()"/>
		
	</mx:RemoteObject>
	
	<mx:HBox width="100%">
		<mx:ComboBox id="imageSelect" prompt="Select an image..." dataProvider="{_imageArray}" change="onImageSelect()"/>
		<mx:Button label="Add Image (Preview)" click="selectImage()"/>
		<mx:TextInput id="imageNameTI" text="{this.previewFileName}" enabled="false"/>
		<mx:Button id="uploadBtn" label="Upload" enabled="false" click="uploadImage()"/>
		<mx:Button id="deleteBtn" label="Delete Selected Image" enabled="false" click="deleteSelectedImage()"/>
	</mx:HBox>
	
	<mx:Canvas id="imgCanvas" width="100%" height="100%"/>
	
</mx:WindowedApplication>


So there you have it, you can now move image data between CF8 and Flex 3 apps without having to make http post or get calls from the Flex app.  Delivering content via binary data / ByteArrays makes it possible to deliver content to your Flex applications without having to expose that content to the public web via a URL, combined with using RemoteObject over SSL you could make a pretty slick and secure application.


Search