Data Compression with ActionScript 3

So, I'm working on a little project that requires building a lot of vectors in a Flash presentation. I'm managing the vectors visually using
Inkscape. Up to this point, I've been translating the vectors using a little PHP script to pull apart the SVG data, and convert all the vector information into array literals formatted for AS3. The ActionScript takes the literals and draws vectors using the graphics library. The vector data is pretty large. It's so large, that I've been pasting the literal data into my ActionScript source files and relying on the SWF text code compression to reduce the size of the data. It works rather well (the uncompressed vector data is about 2x the size of the final SWF, which includes a fair amount of functional code to manipulate the visuals).
But, as an engineer, this looks sloppy. So, I decided to look into a few other ways of handling this. One that seems to be common is using something called the Action Message Format (AMF). The problem here is that the data is encoded and compressed using a proprietary Adobe-designed format. While the format is no mystery, and there are plenty of libraries available for various platforms to encode/decode this information, I wanted to see if Flash could play nice with the rest of the world, instead of making the rest of the world tip-toe around Flash.
It turns out, it's amazingly--almost deceptively--simple.
First, I wrote a tiny test script to kick out some compressed text. In this case, the compression is worthless because the message is very short, but it's just for proof-of-concept testing.
<?php
$message = gzcompress('compressed message for flash', 9);
header('Content-Type: application/x-gzip');
echo $message;
?>
The next part is to try to decompress this information using ActionScript. It only took about 15 minutes of reading the docs to figure out how the
URLLoader class can read this information directly. Then, the
ByteArray class immediately provides the ability to decompress the raw information.
This is all the ActionScript you need to decompress the message:
import flash.events.*;
import flash.net.*;
//Set up the typical URLLoader instance.
var ul:URLLoader = new URLLoader();
//Here, we tell the URLLoader to be ready to catch a bunch of binary data.
ul.dataFormat = URLLoaderDataFormat.BINARY;
//Set up the request to the script generating the test message.
var req:URLRequest = new URLRequest('http://example.com/zlibtest.php');
//Assign the typical response handler.
ul.addEventListener(Event.COMPLETE, function(e:Event) {
//Grab the URLLoader reference.
var ul:URLLoader = URLLoader(e.target);
//This just outputs random characters/garbage.
trace('Raw Data: '+ul.data);
//The ByteArray object already knows how to handle the decompression.
ul.data.uncompress();
//The message comes through perfectly.
trace('Dec Data: '+ul.data.toString());
});
//Fire off the request.
ul.load(req);
And, that's it! Clean, simple, and uses built-in facilities instead of relying on tons of ported libraries bloating the SWF.
One obvious application is compressing and decompressing large XML messages. You're typically using a server-side script to dynamically construct the message. Instead of just sending the XML, pass it through the
gzcompress() function (or whatever your preferred platform calls it), first. In the Flash client, instead of just sending the data directly to the XML object, run a
ByteArray::uncompress() on it, then pass it into the XML object. It doesn't get much simpler.
You might think this is so simple and great that it needs to be used everywhere. But, wait! There are a few considerations.
First, compressing and decompressing things isn't free. The savings in bandwidth are paid for by additional CPU/memory usage on both ends. HTTP servers are typically not bottle-necked for processing resources, so it's probably negligible in that regard. Flash clients, however, might need to consider this within the context of the entire presentation. Is the presentation already hogging a lot of resources on the visitors' computers? If so, maybe this method is okay for one or two messages, but not a constantly-updating client, like a chat client or multiplayer game.
Second (and related to fore mentioned "noisy" clients), small messages don't compress that well. If your messages are all less than 200 bytes, or so, the savings are probably not worth it. If your messages are things like single sentences, then you're actually incurring more data transfer (due to format headers).
One last thing about real-world implementation: You might want to abstract this process in your clients to provide a reasonable "fall back" if the server is not able to provide a compressed message. HTTP includes facilities for this through the
Accept request header. Your client could specify that it prefers compressed messages, and then deal with whatever the server gives you based on the
Content-Type response header. It complicates the solution a tad, but it might be worth it to have that loosely-coupled design many of us enjoy.
Happy coding!
Comments
There are currently no comments.