Probably old hat to many folks, but I had to do a bit of googling yesterday to figure this one out and thought I’d write it down lest I forget it all again.
The first gotcha to watch out for (the one that got me, anyway), is making sure that AMFPHP is encoding in AMF3. This can be set in the constructor of the AMFPHP class.
Your AMFPHP class may look like this, for instance:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<?php class ByteSaver { function ByteSaver() { $GLOBALS['amfphp']['encoding'] = 'amf3'; mysql_pconnect("SERVER", "NAME", "PASS"); mysql_select_db("DB"); } /** * Grabs byte array from database table named 'bytearray' and sends it back to Flash * @returns ByteArray */ function getData() { $result = mysql_query("SELECT * FROM bytearray LIMIT 1"); $row = mysql_fetch_assoc( $result ); return new ByteArray($row['byte_array']); } /** * Saves ByteArray from Flash to database table 'bytearray' (in this case the 'byte_array' field is a longblob). * @returns ArrayCollection of all images */ function setData($ba) { $result = mysql_query("INSERT INTO bytearray (byte_array) VALUES ('$ba->data')"); return $result; } } ?> |
The actionscript is pretty standard AMFPHP/actionscript stuff. The question is: why would you need this? Well, say you have a very complex bit of data containing an indeterminate number of objects in multi dimensional array(s) and you want to save all this as a single object in a single database field. You could manually serialize the mess and write it as one long string – or you could simply write the object to a ByteArray and save it in a ‘blob’ in your database.
Here’s a quick example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
package { import flash.display.Sprite; import flash.events.Event; import flash.net.NetConnection; import flash.net.Responder; import flash.utils.ByteArray; /** * send then receive bytearray from database example * @author Devon O. Wolfgang */ public class Example extends Sprite { private var resp1:Responder; private var resp2:Responder; private var nc:NetConnection; public function Main():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // entry point var author1:Object = { firstname : "James", surname : "Ballard" } var author2:Object = { firstname : "Hunter", surname : "Thompson" } var author3:Object = { firstname : "Chuck", surname : "Palahniuk" } var author4:Object = { firstname : "William", surname : "Gibson" } var living:Array = [author3, author4]; var dead:Array = [author1, author2]; var allAuthors:Array = [living, dead]; var ba:ByteArray = new ByteArray(); ba.writeObject(allAuthors); ba.position = 0; resp1 = new Responder(onGoodSave, onBadReturn); resp2 = new Responder(onGoodReturn, onBadReturn); nc = new NetConnection(); nc.connect("http://localhost/amfphp/gateway.php"); // send bytearray to database nc.call("ByteSaver.setData", resp1, ba) } // once bytearray has been sent to server private function onSave(o:Object):void { // if bytearray was inserted into database if (String(o) == "true") { nc.call("ByteSaver.getData", resp2); } else { trace ("data not added to database for whatever reason"); } } // when you retrieve bytearray from server private function onGoodReturn(o:Object):void { var ba:ByteArray = o as ByteArray; ba.position = 0; trace (ba.readObject()[0][1].surname); // Gibson } // bad amfphp call private function onBadReturn(o:Object):void { for (var prop:String in o) trace (prop, o[prop]); } } } |
Hope that might help someone out. My future self if no one else.
In other news, just saw that my Site GatherAIR thingamabob has now been listed over on Refreshing Apps. Very cool stuff! And thanks to whoever submitted it.
No I found it useful. I have a large project that has been doing this process and also went through the googling phase. I can tell you that this article is one of those that really needs to be indexed on google.
This is brilliant – thank you. It’s very good of you to share this and I owe you a beer!!!
When you bring in the bytearray object from the server though, i think you can just cast it to bytearray in flash straight away. So towards the end of the as script it ends up something like this:
private function onGoodReturn(o:ByteArray):void { …
No need to cast thereafter. Very picky but emphasises the power and ease of access with amfphp.
Very good tip, Sean. Didn’t think to skip that one extra step of casting the return. Very cool!
whats the field type for “byte_array”? Is the example done in flex?
Thx for the post, very interesting.
When i pull the byte array in from the database and assign it to a byte array var i get the following:
cannot convert Object@1ecd80d9 to flash.utils.ByteArray.
In my amfphp browser the object looks like this:
(flash.utils::ByteArray)#2
bytesAvailable = 291
endian = “bigEndian”
length = 291
objectEncoding = 3
position = 0
does this look right compared to what you get? Also, have u been going to flash on the beach? if so, i may c u there tomorrow :o
Strange that the browser shows it as a ByteArray, but you can’t type it as one. Been so long since I’ve touched this, I forgot about it (needed it for work the day I blogged this and haven’t touched it since).
I have been at FOTB. Gonna be at the party at Oceania tonight. I’m the Flash Cowboy.. :)
I was getting the dreaded NetConnection.call.badversion occasionally when adding a specific string to my multidimensional array. Specifically, “Lucky Number Slevin” just as a test. It was driving me crazy because it would work all the other times. I finally figured out that I needed to escape the bytearray with mysql_escape_string(). So hopefully this will help others out with intermittent badversion errors.
I meant mysql_real_escape_string of course.