One of the challenges with occasionally connected applications is the need for dynamic images. Dynamic images are easy when the application is run while connected to the internet, but will result in broken images, if the machine is not connected.
To solve this problem, we use an ImageCacheManager, which checks if the file is cached locally first, before trying to load it from the internet. If the local version exists, it is used, other wise, the remote version is loaded and cached
First, we implement the ImageCacheManager, which works as a singleton. Then, any requests for images which should be cached are made by specifying the source through the image cache manager:
<mx:Image source="{ImageCacheManager.getInstance().getImageByURL('http://i2.cdn.turner.com/cnn/2008/WORLD/americas/08/26/hurricane.gustav/t1home.gustav.map1.noaa.jpg')}"/>
package managers
{
import flash.filesystem.File;
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.events.Event;
import flash.net.URLLoaderDataFormat;
import flash.utils.Dictionary;
import flash.filesystem.FileStream;
import flash.filesystem.FileMode;
import com.adobe.crypto.MD5;
public class ImageCacheManager
{
private static const imageDir:File = File.applicationStorageDirectory.resolvePath("cachedimages/");
private static var instance:ImageCacheManager;
private var pendingDictionaryByLoader:Dictionary = new Dictionary();
private var pendingDictionaryByURL:Dictionary = new Dictionary();
public function ImageCacheManager()
{
}
public static function getInstance():ImageCacheManager
{
if (instance == null)
{
instance = new ImageCacheManager();
}
return instance;
}
public function getImageByURL(url:String):String{
var cacheFile:File = new File(imageDir.nativePath +File.separator+ cleanURLString(url));
if(cacheFile.exists){
return cacheFile.url;
} else {
addImageToCache(url);
return url;
}
}
private function addImageToCache(url:String):void{
if(!pendingDictionaryByURL[url]){
var req:URLRequest = new URLRequest(url);
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE,imageLoadComplete);
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.load(req);
pendingDictionaryByLoader[loader] = url;
pendingDictionaryByURL[url] = true;
}
}
private function imageLoadComplete(event:Event):void{
var loader:URLLoader = event.target as URLLoader;
var url:String = pendingDictionaryByLoader[loader];
var cacheFile:File = new File(imageDir.nativePath +File.separator+ cleanURLString(url));
var stream:FileStream = new FileStream();
stream.open(cacheFile,FileMode.WRITE);
stream.writeBytes(loader.data);
stream.close();
delete pendingDictionaryByLoader[loader]
delete pendingDictionaryByURL[url];
}
private function cleanURLString(url:String):String{
var hash:String = MD5.hash(url);
return hash;
}
}
}
Note: The ImageCacheManager makes use the the MD5 class, which is available in the as3corelib.swc (http://code.google.com/p/as3corelib/). This is used not for security or obfuscation purposes, but to ensure the name of the file which is written contains only "safe" characters
+