views:

290

answers:

3

So let's say we want to load some XML -

var xmlURL:String = 'content.xml';
var xmlURLRequest:URLRequest = new URLRequest(xmlURL);
var xmlURLLoader:URLLoader = new URLLoader(xmlURLRequest);
xmlURLLoader.addEventListener(Event.COMPLETE, function(e:Event):void{
 trace('loaded',xmlURL);
 trace(XML(e.target.data));
});

If we need to know the source URL for that particular XML doc, we've got that variable to tell us, right? Now let's imagine that the xmlURL variable isn't around to help us - maybe we want to load 3 XML docs, named in sequence, and we want to use throwaway variables inside of a for-loop:

for(var i:uint = 3; i > 0; i--){
 var xmlURLLoader:URLLoader = new URLLoader(new URLRequest('content'+i+'.xml'));
 xmlURLLoader.addEventListener(Event.COMPLETE, function(e:Event):void{
  trace(e.target.src); // I wish this worked...
  trace(XML(e.target.data));
 });
}

Suddenly it's not so easy, right?

I hate that you can't just say e.target.src or whatever - is there a good way to associate URLLoaders with the URL they loaded data from? Am I missing something? It feels unintuitive to me.

A: 

You could create a simple wrapper object that proxies everything through to your URLRequest and stores an additional property for source_url or something similar. Or you could extend URLRequest and add the custom property.

sberry2A
+2  A: 
for (var i:uint = 3; i > 0; i--) {
    var src:URLRequest = new URLRequest('content'+i+'.xml');
    var xmlURLLoader:URLLoader = new URLLoader(src);
    xmlURLLoader.addEventListener(Event.COMPLETE, function(req:URLRequest):Function {
        return function(e:Event):void {
            trace(req); // Should work
            // whatever you need to do
        }
    }(src));
}

You've gotta use the second function to wrap the request, otherwise all three event listeners will reference the last request.

Cory Petosky
Oh, interesting! yeah, this is exactly the kind of thing I'm looking for. So I'd be passing src into a function which expects the request as an argument, and returns a function which will actually be called with the even... yeah, yeah, totally. I like it.
matt lohkamp
One thing to keep in mind is that these loaders are technically eligible for garbage collection because there's no reference to 'em -- this approach is fine for a couple one-offs, but if you're loading a significant chunk of data it's possible for some loads to just... vanish. Pushing each loader to some garbage array at the instance level is an acceptable workaround. It also serves as a counter too -- just pop the array on every complete, and when the array is empty, all your stuff is done loading.
Cory Petosky
That did occur to me, but in the case of the project I'm currently working on, I'm pretty sure I can get away with it. Keeping a reference around to act as a load progress counter is a good excuse, though, I'll keep that in mind.
matt lohkamp
I've used this in like 3 projects already, by the way - thanks again.
matt lohkamp
+1  A: 

sberry2A is on the right track. Robert Penner recently showed us that URLLoader is nothing but a tiny wrapper around the native URLStream, so rolling your own URLLoader is a reasonable idea.

This is how I roll:

public class URLLoader extends EventDispatcher{     
    private var _urlRequest:URLRequest; //the built-in URLLoader doesn't give you any access to the requested URL...
    private var _stream:URLStream;
    private var _dataFormat:String;// = "text"
    private var _data:*;
    private var _bytesLoaded:uint;// = 0
    private var _bytesTotal:uint;// = 0

    public function get request():URLRequest { return _urlRequest;}     
    public function get fileName():String { return _urlRequest.url.match(/(?:\\|\/)([^\\\/]*)$/)[1];}       
    public function get dataFormat():String { return _dataFormat;}      
    public function get data():* { return _data; }      
    public function get bytesLoaded():uint { return _bytesLoaded; }     
    public function get bytesTotal():uint { return _bytesTotal; }       

    public function URLLoader(request:URLRequest = null){
        super();
        _stream = new URLStream();
        _stream.addEventListener(Event.OPEN, openHandler);
        _stream.addEventListener(ProgressEvent.PROGRESS, progressHandler);
        _stream.addEventListener(Event.COMPLETE, completeHandler);
        _stream.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
        _stream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
        _stream.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
        if (request != null){
            load(request);
        };
    }
    public function load(request:URLRequest):void {
        _urlRequest = request;
        _stream.load(_urlRequest);
    }
    public function close():void{
        _stream.close();
    }

    private function progressHandler(event:ProgressEvent):void {
        _bytesLoaded = event.bytesLoaded;
        _bytesTotal = event.bytesTotal;
        dispatchEvent(event);
    }
    private function completeHandler(event:Event):void{
        var bytes:ByteArray = new ByteArray();
        _stream.readBytes(bytes);
        switch (_dataFormat){
            case "binary":
                _data = bytes;
                break;
            case "variables":
                if (bytes.length > 0){
                    _data = new URLVariables(bytes.toString());
                    break;
                };
            case "text":
            default:
                _data = bytes.toString();
                break;
        };
        trace("URLLoader: (" + fileName + "): " + event.type);
        dispatchEvent(event);
    }
    private function openHandler(event:Event):void {
        trace("URLLoader: ("+fileName+"): " + event.type +" "+_urlRequest.url);
         dispatchEvent(event);
    }
    private function securityErrorHandler(event:SecurityErrorEvent):void {
        trace("URLLoader ("+fileName+"): " + event.type + " - " + event.text);
        dispatchEvent(event);
    }
    private function httpStatusHandler(event:HTTPStatusEvent):void {          
        dispatchEvent(event);
    }   
    private function ioErrorHandler(event:IOErrorEvent):void {
         trace("URLLoader ("+fileName+"): " + event.type + " - " + event.text);
        dispatchEvent(event);
    }       
}

Notice the request and fileName properties. To get the URLRequest from the COMPLETE-event handler, you simply do: var req:URLRequest = URLLoader(evtObj.target).request;

ulfben