Have you ever built an AIR app, mobile or desktop, that needed to display images from a remote server? If so then you might find this custom component which extends <s:Image> to be handy. It works much as the typical <s:Image> component except that it stores those images on the local device in case they need to be viewed again.
Instead of giving the <s:Image> a source you give it the assetURL and localFolder properties. The assetURL works just like the source. But when the image is downloaded it is automatically stored on the device in the applicationStorageDirectory.localFolder.
The next time the user needs to see that image the ImageGate component will first look in the localFolder to see if the requested file is there. If it is then it will display the image without going back out to the remote server again to get the file.
<comps:ImageGate assetURL="path/image.png" localFolder="imageLocalStorage"/>
Instead of giving it the assetURL property you can give it assetURL160, assetURL240, and assetURL320 properties and it will check the current screen resolution to determine which image to display.
<comps:ImageGate assetURL160="path/image160.png" assetURL240="path/image240.png" assetURL320="path/image320.png" localFolder="imageLocalStorage"/>
Note: Jon Campos ( @jonbcampos ) pointed out that the same thing can be achieved with the ContentCache class.
It’s really a quite simple class. Here’s the code in it’s entirety.
package com.polyGeek.comps {
import flash.display.Bitmap;
import flash.display.Loader;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.system.Capabilities;
import flash.utils.ByteArray;
import spark.components.Image;
public class ImageGate extends Image {
private var _urlRequest : URLRequest;
private var _urlLoader : URLLoader;
private var _loader : Loader;
private var _fileStream : FileStream;
private var _url : String;
private var _filename : String;
private var _file : File;
private var _assetURL : String;
private var _assetURL160 : String;
private var _assetURL240 : String;
private var _assetURL320 : String;
private var _localFolder : String;
public function ImageGate() {
super();
}
private function findImage():void {
/**
* The _localFolder must be set in order to proceed.
*/
if( _localFolder == null ) {
return;
}
var gotAllMultiScreenURLs : Boolean = false;
if( _assetURL160 != null
&& _assetURL240 != null
&& _assetURL320 != null ) {
gotAllMultiScreenURLs = true;
}
/**
* If we don't have either of the _assetURL or all of the
* multi-screen URLs then we can not proceed.
*/
if( _assetURL == null && !gotAllMultiScreenURLs ) {
return
}
/**
* Check to see what the _url is going to be for this particular image.
* -If _assetURL != null then use that url.
* -Otherwise find the correct _url based on the current screen resolution.
*/
if( _assetURL != null ) {
_url = _assetURL;
} else if( Capabilities.screenDPI >= 280 ) {
_url = _assetURL320
} else if( Capabilities.screenDPI >= 200 ) {
_url = _assetURL240
} else {
_url = _assetURL160
}
_filename = _url.substring( _url.lastIndexOf( '/' ) + 1 );
_file = File.applicationStorageDirectory.resolvePath( _localFolder + '/' + _filename );
if( _file.exists ) {
var byteArray : ByteArray = new ByteArray();
_fileStream = new FileStream();
_fileStream.open( _file, FileMode.READ );
_fileStream.readBytes( byteArray );
_fileStream.close();
_fileStream = null;
_file = null;
_loader = new Loader();
_loader.contentLoaderInfo.addEventListener( Event.COMPLETE, onBytesLoaded );
_loader.loadBytes( byteArray );
} else {
downloadRemoteFile();
}
}
private function onBytesLoaded( e:Event ):void {
this.source = new Bitmap( e.target.content.bitmapData );
_loader.contentLoaderInfo.removeEventListener( Event.COMPLETE, onBytesLoaded );
// Cleanup
_loader = null;
_filename = null;
}
private function downloadRemoteFile():void {
_urlLoader = new URLLoader();
_urlRequest = new URLRequest( _url );
_urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
_urlLoader.addEventListener( Event.COMPLETE, onDownloadComplete );
_urlLoader.addEventListener( IOErrorEvent.IO_ERROR, onIOerror );
_urlLoader.load( _urlRequest );
}
private function onDownloadComplete( e:Event ):void {
var byteArray : ByteArray = _urlLoader.data;
_fileStream = new FileStream();
_fileStream.open( _file, FileMode.WRITE );
_fileStream.writeBytes( byteArray, 0, byteArray.length );
_fileStream.close();
_loader = new Loader();
_loader.contentLoaderInfo.addEventListener( Event.COMPLETE, onBytesLoaded );
_loader.loadBytes( byteArray );
// Cleanup
_urlLoader.close();
_urlLoader = null;
_fileStream = null;
_urlRequest = null;
_url = null;
}
private function onIOerror( e:IOErrorEvent ):void {
trace( "image download error : " + _url + " : " + _filename );
// Cleanup
_urlLoader.close();
_urlLoader = null;
_fileStream = null;
_filename = null;
}
/* ************************************************************
* Setters
* ************************************************************ */
public function set assetURL( value:String ):void {
if( _assetURL == value ) {
return;
}
_assetURL = value;
findImage();
}
public function set localFolder( value:String ):void {
if( _localFolder == value ) {
return;
}
_localFolder = value;
findImage();
}
public function set assetURL160( value:String ):void {
if( _assetURL160 == value ) {
return;
}
_assetURL160 = value;
findImage();
}
public function set assetURL240( value:String ):void {
if( _assetURL240 == value ) {
return;
}
_assetURL240 = value;
findImage();
}
public function set assetURL320( value:String ):void {
if( _assetURL320 == value ) {
return;
}
_assetURL320 = value;
findImage();
}
/* ************************************************************
* Getters
* ************************************************************ */
public function get assetURL():String { return _assetURL; }
public function get localFolder():String { return _localFolder; }
public function get assetURL160():String { return _assetURL160; }
public function get assetURL240():String { return _assetURL240; }
public function get assetURL320():String { return _assetURL320; }
}
}




Nice. Thanks !
Download link not working.
@Lionel Sorry. WordPress did something weird with the link. It’s working now.
This component sound really good, however doesnt work for me. Im geting the followinv error in my renderer:
Cannot resolve attribute assetURL for component type spark.components.Image.
I’m not sure what’s up with that. Can you try stepping through the code and seeing where it’s failing inside the component. I know it works because others have used it as well without problems.
Thanks for your answer, maybe is how I implemented it. Could you put a download link to a working project with just one view using your component? It will be really helpfully.
I updated the post and put an example of the component being used. Let me know if this doesn’t help you solve your problem and I’ll create an example project.
Thank you Dan!
Now It works fine. I didn’t have this part in my code: “localFolder=”imageLocalStorage”/”
You can still put an example project to help newbies like me :)
That would be sorta hard. I’d have to create an AIR app because this won’t work in the browser.