AS3 delete a file once it's been accessed via URLLoader - actionscript-3

In AS3 in AIR I'm using a URLLoader to load json data from a file for parsing. Then I want to add objects to the data and re-write the same file, using WRITE and not APPEND. The AIR compiler says, essentially "no can do, the file's in use."
Nullifying the data or closing the URLLoader before re-writing or deleting the file don't work.
How do you get control of a file once a URLLoader has loaded it's data?

Use FileStream instead of URLLoader, and you won't need to use a hacky 0ms timer.

Well, I see that this has been solved by others before. A very slight delay (actually a timer of 0 milliseconds!) is all that’s needed before the delete function to make this problem go away.
Interestingly, if the timer is left out of the wait() function and deleteIt() is called from there instead, the problem returns.
import flash.filesystem.*;
import flash.net.URLLoader;
var json:URLLoader
function loadMyJSONData():void
{
json = new URLLoader();
json.addEventListener(Event.COMPLETE, wait);
json.load(new URLRequest("jsondata.json"));
}
function wait(e)
{
var timer:Timer = new Timer(0, 1);
timer.addEventListener(TimerEvent.TIMER, deleteIt);
timer.start();
//deleteIt();
}
function deleteIt(e)
{
var fileToDelete:File = new File();
fileToDelete.nativePath = "C:\\AIR Test 2";
fileToDelete = fileToDelete.resolvePath("jsondata.json");
fileToDelete.deleteFile();
}

Related

as3 Sound Class Unloading Sound

I have tried this for HOURS. There must be a simple solution to stop the sound and unload it in as3.
This is not all of my code, but in short I am loading random sounds. I need certian vars to be outside of the function so I can reference them with other functions for a progress bar.
How do I unload a sound so I can load a new one using the same names without getting an error the second time its called?
I have two buttons in this test. A play Sound and a Stop Sound Button.
here is my code:
var TheSound:Sound = new Sound();
var mySoundChannel:SoundChannel = new SoundChannel();
PlayButton.addEventListener(MouseEvent.CLICK, PlaySound);
StopButton.addEventListener(MouseEvent.CLICK, Stopsound);
function PlaySound(e:MouseEvent)
{
TheSound.load(new URLRequest("http://www.MyWebsite.com/Noel.mp3"));
mySoundChannel = TheSound.play(0);
}
function StopSound(e:MouseEvent)
{
delete TheSound;
}
HERE IS THE ERROR I GET:
Error: Error #2037: Functions called in incorrect sequence, or earlier call was unsuccessful.
at flash.media::Sound/_load()
at flash.media::Sound/load()
at Untitled_fla::MainTimeline/PlaySound()[Untitled_fla.MainTimeline::frame1:21]
UPDATE.... I TRIED STOPING THE SOUND AND THEN UNLOADING IT AS FOLLOWS
mySoundChannel.stop();
TheSound.close();
BUT NOW I GET THIS ERROR:
Error: Error #2029: This URLStream object does not have a stream opened.
at flash.media::Sound/close()
at Untitled_fla::MainTimeline/shut1()[Untitled_fla.MainTimeline::frame1:35]
I believe I am closer. Thanks so much for your help so far.
In order to stop the sound from playing, you first have to tell the SoundChannel instance to stop like so :
mySoundChannel.stop();
Once you did that, you can close the stream used by the sound instance by invoking the close method like so :
TheSound.close();
Also, the delete keyword is rarely used in as3 and you should not it while some methods are trying to access the variable you are deleting. If you want to dispose of the instance that is currently assigned to your TheSound variable, you should set its value to null. This way, flash will properly garbage collect the old Sound instance that is no longer used when it finds the appropriate time to do so.
You can initialise the variable outside of the function, but define it as a new Sound object each time the function is called. That way it has global scope, and you can load a new URL at any time.
var TheSound:Sound;
var mySoundChannel:SoundChannel = new SoundChannel();
PlayButton.addEventListener(MouseEvent.CLICK, PlaySound);
StopButton.addEventListener(MouseEvent.CLICK, StopSound);
function PlaySound(e:MouseEvent)
{
TheSound = new Sound();
TheSound.load(new URLRequest("http://www.MyWebsite.com/Noel.mp3"));
mySoundChannel = TheSound.play(0);
}
function StopSound(e:MouseEvent)
{
mySoundChannel.stop();
TheSound.close()
}

How to duplicate loaded *.swf without setting a class name

I have read this article about abstracting assets from ActionScript:
Abstracting Assets from Actionscript in AS3.0 – Asset Libraries and DuplicateMovieClip
But it requires to set the Linkage Class name. How can I get the same result without setting the linkage class name?
What I want to do is to cache a loaded asset, and use the cached version every time I request the same URL. A solution is to clone the loaded DisplayObject, but I think it's unnecessary since I only want a new copy.
I think the way to do that is to use byte arrays
here's a quick sample
// once you load your data...
private function loaderComplete(event:Event):void
{
var loaderInfo:LoaderInfo = LoaderInfo(event.target);
var byteArray:ByteArray = loaderInfo.bytes; //<- this will create your byte array
}
you can then use byteArray.readObject(); to generate the new class;
look at senocular's post at http://www.kirupa.com/forum/showthread.php?p=1897368
where he's got a function like this:
function clone(source:Object):* {
var copier:ByteArray = new ByteArray();
copier.writeObject(source);
copier.position = 0;
return(copier.readObject());
}
//that you use with
newObjectCopy = clone(originalObject);
hope this gets you started
As of Flash 11.3, there's a function named getQualifiedDefinitionNames that tells me exactly what linkage names should I use with getDefinition, so there's no need to know the values beforehand.

ActionScript: How to Import XML data as a variable (not as an Event)

I am attempting to import some simple XML data into Flash ActionScript 3.0. I can easily do this as an import that is posted to the stage, but I want to save it as a global variable instead. Here is the XML file that I am pulling from:
<utilitySavings>
<nameof file="academicWaterSavings">
<waterValue>100</waterValue>
<elecValue>200</elecValue>
</nameof>
<nameof file="dormWaterSavings">
<waterValue>300</waterValue>
<elecValue>400</elecValue>
</nameof>
<nameof file="greekWaterSavings">
<waterValue>500</waterValue>
<elecValue>600</elecValue>
</nameof>
<nameof file="totalWaterSavings">
<waterValue>1500</waterValue>
<elecValue>1600</elecValue>
</nameof>
...and here is the actionscript:
var req:URLRequest = new URLRequest("data.xml");
var loader:URLLoader = new URLLoader();
var utilitySavings:XML;
function xmlLoaded(event:Event):void
{
utilitySavings = new XML(loader.data);
academicWater.text = utilitySavings.nameof[0].waterValue;
academicElec.text = utilitySavings.nameof[0].elecValue;
var dormWater:String = utilitySavings.nameof[1].waterValue;
trace (dormWater);
}
loader.addEventListener(Event.COMPLETE, xmlLoaded);
loader.load(req);
trace(academicWater.text);
Notice the 'trace (dormWater)' I want to trace this outside of the function so it is accessible in later in my script. I can trace within the function, but this does me no good. I also am able to get the dynamic text to show up on the stage but, likewise, this does me little good.
I appreciate any help or insights.
I can see a couple of ways of achieving this , if you want to create a globally accessible Object, create a Singleton( not recommended ) , load your XML data into it then every object in your app will be able to access the loaded XML data.
http://www.gskinner.com/blog/archives/2006/07/as3_singletons.html
The resulting code would give you something like this:
//Available throughout your app after the XML has been loaded & parsed
var dormWater:String = Singleton.dormWater;
Although you state you don't want Events, I think that using Signals could be a better approach. Load your XML and dispatch a Signal containing the relevant String to the object that needs it, when receiving the Signal , assign the String to a variable.
http://www.peterelst.com/blog/2010/01/22/as3-signals-the-best-thing-since-sliced-bread/
//In a specific class
private var _dormWater:String;
private function signalListener(value:Object ):void
{
_dormWater = value.dormWater;
}

AS3: return array from event listener?

I have an event listener applied to an xml load and it currently traces out the values it grabs which is fine, but what I want it to do is return an array for me to use. I have the Array creation and return working from "LoadXML" (it returns the array) but I can't get this to work with an event listener.
The event listener runs the "LoadXML" function fine, but I have no idea how to take the returned array for use, this is an example of how my event listener works right now:
xmlLoader.addEventListener(Event.COMPLETE, LoadXML());
and my assumption of how I would take the array (this doesn't work):
var rArray:Array = xmlLoader.addEventListener(Event.COMPLETE, LoadXML());
so instead I tried the following:
xmlLoader.addEventListener(Event.COMPLETE, function():Array{
var rData:Array = LoadXML(datahere);
return rData;
}
but that doesn't worth either.
So: How do I return an array from an eventlistener? Thanks!
I think there is some confusion of how event listeners work. Actually, I'm surprised your not getting compile errors with your current code.
When adding an event listener, what you should be passing in is a reference to a function to be called at a later time. Then when that function gets called, it will pass an Event object with any retrived data for working with. Here is a example:
xmlLoader.addEventListener(Event.COMPLETE, handleLoadComplete/*Note: No brackets, this is a reference*/);
//will be called at a later time, not instantly.
function handleLoadComplete(e:Event):void {
var xml:XML = xmlLoader.data as XML;
//do what ever you want this the XML...
}
Hopefully that makes things clearer for you.
Happy Coding!
Why not just use a component-level object and set its value (xml contents in your LoadXML() method)?
var rArray:Array;
xmlLoader.addEventListener(Event.COMPLETE, LoadXML);
private function LoadXML(event:Event=null):void {
// set this.rArray in here...
}
It's possible to make returnedArray contain an array created by convertXML, but not in the way you're trying to do it. Flash simply doesn't work that way.
This is roughly what happen when you run the code from pastebin:
Start running function loadInformation()
var returnedArray:Array = loadinformation("http://website.com/xml.xml");
Tell Flash that when xmlLoader loads completely, it should run LoadXML()
xmlLoader.addEventListener(Event.COMPLETE, LoadXML);
Start loading an XML file
xmlLoader.load(new URLRequest(xmlurl));
Tell Flash what LoadXML() is (and convertXML())
function LoadXML(e:Event):void {...}
Stop running function loadInformation()
Flash goes off and does other stuff while waiting for the XML file to load
The XML file finishes loading. Flash calls LoadXML() like it was told to.
Note that LoadXML() is being called by Flash, not by loadInformation().
LoadXML() processes the XML file.
To get the converted array data, you need to do something like clownbaby's answer: set the value of returnedArray directly while inside LoadXML.
var returnedArray:Array;
loadinformation("http://website.com/xml.xml");
function loadinformation(xmlurl:String):Array{
var xmlLoader:URLLoader = new URLLoader();
var xmlData:XML = new XML();
xmlLoader.addEventListener(Event.COMPLETE, LoadXML);
xmlLoader.load(new URLRequest(xmlurl));
}
function LoadXML(e:Event):void {
xmlData = new XML(e.target.data);
returnedArray = convertXML(xmlData);
}
function convertXML(xml):Array{
// Does some stuff with the XML and returns an array
return rArray;
}

Reading in a File (AS3) and repeatedly/dyamically accessing the data

This may be a tired old question, but I have yet to find a good answer. Say for instance you have a class that reads in an xml file to get information such as a grocery store items, prices, etc. This class also allows you to retrieve the information about a grocery store item with a get() function.
var grocery:GroceryStore = new GroceryStore(); //create a class that
//reads in xml about
//grocery items
grocery.get("lettuce"); //get some data
In this scenario, I am constantly running into issues because the get() function is being called before the event that loads in the xml file. It wouldn't make sense to place the get() in the onLoad event for the xml file because I want it to be re-usable and dynamic. Also, AS3 doesn't have a wait() function so I can't stall until the file is loaded? Does anyone have an idea on how to read in a file and then be able to safely access the data dynamically and repeatedly? Hopefully this example and my question is thorough enough, if not let me know.
Thanks
You can use events - listen for the complete event to be dispatched.
Add the following code to GroceryStore class
//constructor or a load method
var ldr:URLLoader = new URLLoader();
ldr.addEventListener(Event.COMPLETE, onLoad);
ldr.load(new URLRequest(xmlurl));
function onLoad(e:Event):void
{
//process xml here
dispatchEvent(e);
}
Now use it as:
var grocery:GroceryStore = new GroceryStore();
grocery.addEventListener(Event.COMPLETE, onGroceryLoad);
function onGroceryLoad(e:Event):void
{
grocery.get("lettuce");
}