Returning a byte string to ExternalInterface.call throws an error
Asked Answered
D

2

8

I am working on my open source project Downloadify, and up until now it simply handles returning Strings in response to ExternalInterface.call commands.

I am trying to put together a test case using JSZip and Downloadify together, the end result being that a Zip file is created dynamically in the browser, then saved to the disk using FileReference.save. However, this is my problem:

The JSZip library can return either a base64 encoded string of the Zip, or the raw byte string. The problem is, if I return that byte string in response to the ExternalInterface.call command, I get this error:

Error #1085: The element type "string" must be terminated by the matching end-tag "</string>"

ActionScript 3:

var theData:* = ExternalInterface.call('Downloadify.getTextForSave',queue_name);

Where queue_name is just a string used to identify the correct instance in JS.

JavaScript:

var zip = new JSZip();
zip.add("test.txt", "Hello world!\n");
var content = zip.generate(true);
return content;

If I instead return a normal string instead of the byte string, the call works correctly.I would like to avoid using base64 as I would have to include a base64 decoder in my swf which will increase its size.

Finally: I am not looking for a AS3 Zip generator. It is imperative to my project to have that part run in JavaScript

I am admittedly not a AS3 programmer by trade, so if you need any more detail please let me know.

Dyslexia answered 21/1, 2010 at 5:57 Comment(3)
Looks nice! I've no idea how to answer your question, but thought I'd point out a typo on downloadify.info - "its time to make broad use of this feature" should be "it's time to make broad use of this feature".Blaylock
@Dominic Rodger Thanks man... its fixed! Computer languages, I am cool with. Human languages, well, thats different :)Dyslexia
np - always happy to be a pedant!Blaylock
R
3

When data is being returned from javascript calls it's being serialized into an XML string. So if the "raw string" returned by JSZip will include characters which make the XML non-valid, which is what I think is happening here, you'll get errors like that.

What you get as a return is actually:

<string>[your JSZip generated string]</string>

Imagine your return string includes a "<" char - this will make the xml invalid, and it's hard to tell what character codes will a raw byte stream translate too.

You can read more about the external API's XML format on LiveDocs

Rhys answered 21/1, 2010 at 11:59 Comment(3)
+1 But how would he go about fixing it? Iterating the bytestream to replace 0x3C (<) with 0x26 0x6C 0x74 0x3B (&lt;) and so on in javascript and back in flash? Will that work/is that practical?Quadratic
I would just go with the base64 encoded string, as the library supports, and it's a good, safe way for encoding data as strings. There's a AS3 library that supports decoding and encoding base64 data at github.com/spjwebster/as3base64 .Rhys
That starts to make sense now why it was throwing what seemed to be a XML error. I do have it working with base64 now, I was just hoping to avoid that step. I guess its just as well, because thats how I will end up getting data from the HTML5 Canvas element. Thanks so much for your help and your great answer +1.Dyslexia
M
1

i think the problem is caused by the fact, that flash expects a utf8 String and you throw some binary stuff at it. i think for example 0x00FF will not turn out to be valid utf8 ...

you can try fiddling around with flash.system::System.setCodePage, but i wouldn't be too optimistic ...

i guess a base64 decoder is probably really the easiest ... i'd rather worry about speed than about file size though ... this rudimentary decoder method uses less than half a K:

public function decodeBase64(source:String):ByteArray {
 var ret:ByteArray = new ByteArray();
 var map:Object = new Object();
 var i:int = 0;
 for each (var char:String in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("")) map[char] = i++;
 map["="] = 0;
 source = source.split("\n").join("").split("\r").join("");//remove linebreaks
 for (i = 0; i < source.length/4; i++) {
  var buf:int = 0;
  for each (char in source.substr(i * 4, 4).split("")) buf = (buf << 6) + map[char];
  ret.writeByte(buf >>> 16);
  ret.writeShort(buf);
 }
 return ret;
}

you could simply shorten function names and take a smaller image ... or use ColorTransform or ConvolutionFilter on one image instead of four ... or compile the image into the SWF for smaller overall size ... or reduce function name length ...

so unless you're planning on working with MBs of data, this is the way to go ...

Middleaged answered 21/1, 2010 at 13:41 Comment(2)
oops ... forgot to actually post the answer ... well anyway, it might be of some help ... ;)Middleaged
You are right, even adding the Encoded/Decoder library @Robert mentioned above, added only 1K to my finished SWF. I think I can live with that. Thanks for your time!Dyslexia

© 2022 - 2024 — McMap. All rights reserved.