Introduction

"And soon I will have understanding of videocassette recorders and car telephones. And when I have understanding of them, I shall have understanding of computers. And when I have understanding of computers, I shall be the Supreme Being!" (Time Bandits 1981)

Chris Ellis is a freelance Senior Flash Programmer, former Lingo Programmer and resident of London.

He has an MSc in Interactive Multimedia from Westminster University, graduating in 2000. He started programming at the age of 8 with a BBC Model B and a few books on BASIC.

What follows are some snippets from what he's currently up to.

Wednesday, 2 September 2009

Saving Binary Data - better than XML?

Appologies for the formatting of the code coming up. This is the first time I have put up code on Blogger and probably the last as it has almost no options to let me publish it in a readable format.

The project I was working on was a microsite for the Sky Animation Season. To give you a taste here are a few screen shots:





You can see it in all it's glory (if it's still live at the time of reading) here:
Sky Animation Season

Basically the main part of the site which is of interest for this article was the section where a user can create a flickbook by drawing lines (the second image above). They can have up to 100 pages and there is not really a limit on the number of lines they can draw for each page. This data needed to be stored for the show a friend section.

My normal initial reaction when saving data is to use XML as it's easy to use and everyone knows what it is. In this case though each line would have a start point, end point, colour, thickness and page number. Once a user draws a curve, the lines really start to add up! I could of course save each page as bitmap data as it didn't need to be re-edited but 100 pages of bitmap data is a lot of data as well and test were showing that it was going to be a bit slow.

For this project we were going to use AMFPHP as the backend and somewhere along the line when I was reading something about AMFPHP I read something about saving data as binary arrays and decided that this was going to be a much better way to go. I have a load of data which I wanted to save as a block, why can't I just put that wad of data into a database and then read it back out when I wanted it? Well, I could! Basically we could convert all the data into a binary array, send that to the database, which only had to expect one entry (the binary data) and just do the reverse when we wanted the data back. This would mean the size of the data would be really small and fast. There were other benefits too which I will sum up at the end.

So, how did we do it? Firstly by looking on google! There were a couple of good but not complete tutorials on similar subjects. I can't remember what they were now to give credit to them, but if you search for 'saving binary data in amfphp' you should stumble upon them pretty quickly.

Here is the crux of the code:

Firstly you have to register your classes. As far as I can tell this adds a little data to them so that when they are turned back into classes from binary data, they remember what objects they were.

registerClassAlias("com.skymovies.flipbook.model.vo.Line",Line);
registerClassAlias("com.skymovies.flipbook.model.vo.SendDetails",SendDetails);
registerClassAlias("com.skymovies.flipbook.model.vo.FlipBookLines",FlipBookLines);

This only really works on value objects as far as I can tell, but these values can be other value objects.
Here's an example of a value object:

package com.skymovies.flipbook.model.vo {
    class FlipBookLines {
     public var arrLines:Array;//an array of Line value objects
     public var sendDetails:SendDetails;//send details is another value object
     public function FlipBookLines() {
     }
}
}


So this is how to convert the data into an array and send it. Some of this is project specific but you should get the gist. Don't forget to import the relevant classes if you're using some dodgy IDE that doesn't do this for you.


public function sendData():void{
    //get the line data
     var voFlipBook:FlipBookLines = getFlipbookData();
    var byteArray:ByteArray = new ByteArray();
    byteArray.writeObject(voFlipBook);
    byteArray.compress();

    //pass array of lines to amfphp
    myService = new NetConnection();
    NetConnection.defaultObjectEncoding = ObjectEncoding.AMF3;
    myService.connect('../../gateway.php');//the path to the gateway.php file
    //the responder handles the server response. The arguments are the functions that will be called
    var responder:Responder = new Responder(onResult, onFault);
    //call(className.methodName, responder object, line data);
    myService.call("FlipBookLines.putFlipBook", responder, byteArray);
}


So that sends the data as a single blob of binary, how is it retrieved?


//retreive data like this
public function retrieveData() : void {
    //call the amfphp service
    var myService:NetConnection = new NetConnection();
    NetConnection.defaultObjectEncoding = ObjectEncoding.AMF3;
    myService.addEventListener(NetStatusEvent.NET_STATUS,netError);
    myService.connect(drawMod.gateway);
    var responder:Responder = new Responder(onLinesLoaded, onFault);
    //call(className.methodName, responder object, line data, send data);
    myService.call("FlipBookLines.getFlipBook", responder, drawMod.saved_flipbook_id);
}

//once the response has been got it gets a little more interesting
private function onLinesLoaded(result:Object):void{
    if (! (result is ByteArray)) {
     throw new Error("Result is not a ByteArray. Check AMFPHP 'encoding' property");
    }
    var ba:ByteArray = result as ByteArray;
    if (! ba) {
     throw new Error("An error occurred: " + result.toString());
    }
    //decompress
    ba.uncompress();
    //read object turns it back into a FlipBookLines object
    var fbLines:FlipBookLines = ba.readObject() as FlipBookLines

//
    if (! fbLines) {
     throw new Error("An error occurred.");
    }else{
     //success
     dispatchEvent(new Event(FLIPBOOK_LOADED));
    }
}


Eh voila we have a value object back just as it was in the begining which makes it nice and easy to redraw the screens.
The php service is very simple as well. Here are the bones of it:

Php
public function putFlipBook($arrLines){
$this->connect();
$arrLines = $arrLines->data;
$arrLines = mysql_real_escape_string($arrLines);

$query = "INSERT into linedata (picture_id, arrlines) VALUES ('$hash', '$arrLines')";
$result = mysql_query($query);
$error = mysql_error();
$recnum = mysql_insert_id();
mysql_close();
if ($error)
return "error: " . $error;
else
return $hash;
}



One important thing to note that in the database, the data needs to be saved as a BLOB (Binary Large Object)!

So what are the benefits of doing it in like this?
Data can be changed in the AS; new items can be added, existing items can be changed without any change needed to the back end.
Low data size vs xml. Much quicker to save, load.
Items are converted back to objects on load, which cuts down parsing xml and creating new objects and setting values.

Its not all a bed of roses though, there could be potential problems.
Nothing else can use the data. If you are collecting names and addresses this isn't any use.
If it needs manually edited or moderated at the database end this can't be done as there is nothing really to inspect.

So thats it. In this case it was really useful and saved me a lot of xml creation and reading and the backend didn't have to do anything either. It won't be great in all circumstances but if you have a piece of data that will only be read by that app that needs to be saved for later retrieval this could be a good option to expore.

0 comments: