Skip to content

Instantly share code, notes, and snippets.

@PrimaryFeather
Last active August 7, 2018 08:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PrimaryFeather/a08cda0c75ae8ff7a660d76cc391628f to your computer and use it in GitHub Desktop.
Save PrimaryFeather/a08cda0c75ae8ff7a660d76cc391628f to your computer and use it in GitHub Desktop.
A custom DataLoader extension that makes the asset manager cache remote files locally.
package starling.extensions
{
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.utils.ByteArray;
import starling.assets.DataLoader;
public class CachingDataLoader extends DataLoader
{
private var _cacheDir:String;
public function CachingDataLoader(cacheDirName:String="starling-asset-cache")
{
_cacheDir = cacheDirName;
}
override public function load(url:String, onComplete:Function,
onError:Function, onProgress:Function = null):void
{
var cacheFile:File;
var stream:FileStream;
var remoteData:ByteArray;
if (url.indexOf("http") == 0)
{
cacheFile = getCacheFile(url);
if (cacheFile.exists)
{
trace("loading from local cache file: " + cacheFile.nativePath);
super.load(cacheFile.url, onComplete, onError, onProgress);
}
else
super.load(url, onLoadComplete, onError, onProgress);
}
else
super.load(url, onComplete, onError, onProgress);
function onLoadComplete(data:ByteArray):void
{
trace("saving to local cache file: " + cacheFile.nativePath);
remoteData = data;
stream = new FileStream();
stream.addEventListener(Event.CLOSE, onFileStreamClosed);
stream.addEventListener(IOErrorEvent.IO_ERROR, onFileStreamError);
stream.openAsync(cacheFile, FileMode.WRITE);
stream.writeBytes(data);
stream.close();
}
function onFileStreamClosed(event:Event):void
{
cleanup();
onComplete(remoteData);
}
function onFileStreamError(error:IOErrorEvent):void
{
trace("error writing asset store cache: " + error.text);
cleanup();
try { stream.close(); } catch (e:Error) {}
try { cacheFile.deleteFile(); } catch (e:Error) {}
onComplete(remoteData);
}
function cleanup():void
{
stream.removeEventListener(Event.CLOSE, onFileStreamClosed);
stream.removeEventListener(IOErrorEvent.IO_ERROR, onFileStreamError);
}
}
private function getCacheFile(remoteUrl:String):File
{
var filename:String = hashString(remoteUrl);
filename = filename.replace("+", "-").replace("/", "_").replace("=", "");
return File.cacheDirectory.resolvePath(_cacheDir + "/" + filename);
}
// -> http://www.cse.yorku.ca/%7Eoz/hash.html
private static function hashString(str:String):String
{
var hash:uint = 5381;
for (var i:int = 0, len:int = str.length; i < len; ++i)
hash = (hash * 33) ^ str.charCodeAt(i);
return hash.toString(16);
}
}
}
@alamboley
Copy link

Hi Daniel, nice extension I was thinking to do something like this the other day ;)

I'm thinking of a way to check if the online ressource has been updated or not. Maybe adding a lapsing variable to the local resource is the simple way to do it. After x days, grabbing the latest source and replace the old one.
Obviously it will be better if we could check the online changed file date, but without server code I don't think it's possible...

@PrimaryFeather
Copy link
Author

Hey @alamboley — sorry, I didn't see your comment sooner!
One simple way to force reloading after a while is to append something like ?version=1 to the resource. That will create a different hash, and so a different name for the cache file. One could simply use the current week number (since, say, 2018/01/01) instead of the version, and so would get a refreshed resource once a week.

To let the server signal that the version has actually changed, it would have to signal the file version in the response header. In Flox, we used the "etag" flag to signal a changed version, i.e. the client would remember the version it has downloaded (i.e. store the "etag" from the last download of the resource) and pass it to the server in the request header; if the version is still the same, the server would reply with Http status "NOT_MODIFIED" (304).

That was a custom implementation, though — I don't know if web servers can be configured like that. Though one would think that there should be such a mechanism; after all, browsers need to check file versions, too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment