1

I've got XML data in AS3 that needs to be compressed, validated on my Java Google App Engine servlet then saved to a file in Google Cloud Storage. Later that file will be opened and decompressed by the AS3 client. The process works if I do it with plain XML or text, but if I ByteArray#compress the data, it dies during ByteArray#uncompress with "There was an error decompressing the data".

I've tried setting the content type and mime type at various points, as well as encoding with Base64, but every attempt seems to break in a different way and I never get the same XML back that I sent in. Do I need to use multipart? Should I compress on the server? What's the best practice for doing this?

Sending the data from AS3:

// compress xml using zlib
var xml:XML = <contents><thing>value</thing></contents>;
var bytes:ByteArray = new ByteArray();
bytes.writeObject(xml);
bytes.position = 0;
bytes.compress();

var request:URLRequest = new URLRequest(url);
var urlVariables :URLVariables = new URLVariables();
urlVariables.filename = "somefile.bin";
urlVariables.contents = bytes;
request.data = urlVariables;
request.method = URLRequestMethod.POST;
loader = new URLLoader();
loader.load(request);

Receiving it in the Java servlet and creating the file:

public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    String filename = req.getParameter("filename");
    byte[] contents = req.getParameter("contents").getBytes();

    GSFileOptionsBuilder optionsBuilder = new GSFileOptionsBuilder()
        .setBucket("bucketname")
        .setKey(filename)
        .setAcl("public-read")
        .setMimeType("binary/octet-stream");
    AppEngineFile writableFile = fileService.createNewGSFile(optionsBuilder.build());
    boolean lockForWrite = true;
    FileWriteChannel writeChannel = fileService.openWriteChannel(writableFile, lockForWrite);
    writeChannel.write(ByteBuffer.wrap(contents));
    writeChannel.closeFinally();
}

Opening the new file in AS3:

var url :String = "http://commondatastorage.googleapis.com/bucketname/somefile.bin";
var request:URLRequest = new URLRequest(url);
request.method = URLRequestMethod.GET;
loader = new URLLoader();
loader.addEventListener(Event.COMPLETE, handleComplete);
loader.load(request);

protected function handleComplete (event:Event):void {
    var bytes:ByteArray = new ByteArray();
    bytes.writeObject(event.target.data);

    // dies on this line with "There was an error decompressing the data."
    bytes.uncompress();

    var xml:XML = new XML(new String(bytes));
    trace(xml);
}
Sarah Northway
  • 1,029
  • 1
  • 14
  • 24
  • maybe try bytes.position = 0 before you un-compress? – The_asMan Apr 10 '12 at 22:06
  • Thanks but didn't help. I tried printing out the value of the bytes just after compressing and again just before decompressing - half the symbols have been replaced by question marks. Must be the encoding, right? – Sarah Northway Apr 10 '12 at 22:29
  • possibly, I would look(httpfox) at the data being sent to the server and compair with whats being sent back. – The_asMan Apr 10 '12 at 22:42
  • Once you run compress the file isn't going to be human readable any longer. One quick thing I can think of would be to try writing the string version of your xml file. xml.toXMLString(); – francis Apr 10 '12 at 22:56
  • It's human-readable enough to tell it has changed. Before: ·ÉI-K?ãRP°)ÈI,IË/Ê-¶34µÑGð@r?å©Åù¹©y©ÅÅvF6úÈ\°Öü?0?3Ôª?0Q?ÍuL?IXZê#I@?@Cmô!n?6= After: ??I-K??RP?)?I,I?/?-?34??G?@r???????y???vF6??\????0?3???0Q??uL?IXZ?#I@?@Cm?!n – Sarah Northway Apr 10 '12 at 23:22
  • Well I see one odd in your code. you are sending data like so urlVariables.contents = bytes; which calls the toString method and when you get the data back from the server you are doing bytes.writeObject(event.target.data); which is basically taking a string and writing it as an object – The_asMan Apr 10 '12 at 23:52
  • If your binary data is getting mangled, it's probably because it's being treated as text somewhere. What do you notice if you compare the streams of binary data in a hex editor? What about if you pass test strings through your roundtrip process? – Nick Johnson Apr 11 '12 at 01:16
  • Also, you're passing your data as both a URL parameter and in the body of the POST request, then ignoring the body data. There's no reason to pass it in the URL - you should pass it in the body only, and fetch it from there. – Nick Johnson Apr 11 '12 at 01:18

2 Answers2

0

Here is the code that I use to save an xml. I send the data to PHP but I would think it would work the same way for you... I haven't had any trouble with it.

var createXMLURL:URLRequest = new URLRequest("createXML.php");
createXMLURL.method = URLRequestMethod.POST;
var Variables:URLVariables = new URLVariables();
Variables.xmlString = xml.toXMLString();
Variables.filename = filename;
createXMLURL.data = Variables;
var loader:URLLoader = new URLLoader();
loader.dataFormat = "VARIABLES";
loader.addEventListener(Event.COMPLETE, xmlCreated);
loader.load(createXMLURL);

Let me know if you have any questions about what some of the variables are since I did not include their declarations (I think they are pretty easy to figure out).

Now this doesn't send that data in binary format like you were asking for, but I don't know why you wouldn't be able to convert the string to binary once you receive it in java if you really need the raw bytes.

M. Laing
  • 1,607
  • 11
  • 25
  • This is just my code with the compression taken out. As I mentioned, that works fine - it's sending in binary format that I'm having a problem with. – Sarah Northway Apr 10 '12 at 23:15
0

I would base64 encode before you POST if from the client, store it that way in a TextProerty, then base64 decode / decompress when received back at the client. If you want to store it as binary on GAE, then base64 decode it into a Blob. Here are some code snippets I pieced together using your code, and something similar I do using HttpService -- apologies in advance for not extensively proofing it. HTH.

private var _serviceHttp:HTTPService = new HTTPService;

private function postBytes():void {
    var xml:XML = <contents><thing>value</thing></contents>;
    var bytes:ByteArray = new ByteArray();
    bytes.writeObject(xml);
    bytes.position = 0;
    bytes.compress();
    var enc:Base64Encoder = new Base64Encoder();
    enc.encodeBytes(bytes, 0, bytes.length);
    var myObj:Object = new Object();
    myObj["bytes"] = enc.toString();
    // myObj["other_variables"] = your_other_varaibles;
    _serviceHttp.method = "POST";
    _serviceHttp.resultFormat = "flashvars";
    _serviceHttp.url = your_url_here;
    _serviceHttp.addEventListener(ResultEvent.RESULT, urlHandler);
    _serviceHttp.addEventListener(FaultEvent.FAULT, urlErrorHandler);
    _serviceHttp.send(myObj);
}
stevep
  • 959
  • 5
  • 8
  • When I add Base64 (which is less compact than plain binary), I end up with extra characters at the start of the decompressed string: _"Wvalue"_ so the call to 'new XML(new String(bytes))' dies with a malformed XML error. – Sarah Northway Apr 11 '12 at 18:40
  • Aha! Finally found the source of that "W", it's some kind of header added by `bytes.writeObject(xml)`. If I use `bytes.writeUTFBytes(xml)` instead, it doesn't appear. – Sarah Northway Apr 11 '12 at 20:03