Pages Menu
Categories Menu

Posted by on Nov 19, 2011 in Code, Mobile, The Cloud |

Using XMLHttpRequest2 in iOS 5 or Android OS 3 to download binary files using HTML5/Phonegap

One of the things added to Safari and UIWebView in iOS5 is support for XMLHttpRequest 2, which according to W3C adds new features “such as cross-origin requests, progress events, and the handling of byte streams for both sending and receiving”. As part of the last bit of that, it’s now possible to set the responseType to arraybuffer, which is “used to represent a generic, fixed-length binary data buffer”. More info here. This is useful if you want to download binary such as image or audio data from a remote location, and potentially manipulate it before presenting it to the user.

This has many possible applications for file transfer, but for instance, if you’re writing an HTML5 / PhoneGap app for iOS 5, and you want to download an attached file from the current version (v23.0) of the Chatter REST API, you need to send a GET request to:

https://test.salesforce.com/services/data/v23.0/chatter/files/[PUT THE FILE ID HERE]/content?versionNumber=1

But, you have to include a few headers for authentication…

setRequestHeader(“Authorization”, “OAuth ” + oauthToken);

setRequestHeader(‘X-User-Agent’, ‘salesforce-toolkit-rest-javascript/v23.0’);

…so it’s not like you can just dump the GET request into the src of an <img> tag. So, what you can do now in iOS 5 is send off an XMLHttpRequest with a responseType of “arraybuffer”, and use this data to write to a HTML5 Canvas object.

Here’s the XMLHttpRequest:

    var request = new XMLHttpRequest();     
    request.open(“GET”, url, true);
    request.responseType = “arraybuffer”;
    request.setRequestHeader(that.authzHeader, “OAuth ” + that.sessionId);
    request.setRequestHeader(‘X-User-Agent’, ‘salesforce-toolkit-rest-javascript/’ + that.apiVersion);
    request.onreadystatechange = function() {
        // continue if the process is completed
        if (request.readyState == 4) {
            // continue only if HTTP status is “OK”
            if (request.status == 200) {
                try {
                    // retrieve the response
                    callback(request.response);
                }
                catch(e) {
                    // display error message
                    alert(“Error reading the response: ” + e.toString());
                }
            }
        }            
    }
    request.send();

And here’s the body of the callback that handles Base64 encoding the arraybuffer and writing that data to a Canvas using the Data URI Scheme:

function(response){
    var imageCanvas = $('#'+value.id);
    var cxt = imageCanvas[0].getContext("2d");

    var myImage = new Image();

    myImage.src = "data:"+value.mimeType+";base64,"+base64ArrayBuffer(response);

    imageCanvas[0].width=myImage.width;
    imageCanvas[0].height=myImage.height;

    cxt.drawImage(myImage,0,0,myImage.width,myImage.height);
}

For the Base64 encoding, I used this handy function that I found over on PasteBin.
Note, I haven’t tested it, but this should (probably) work on Android OS 3.0 (Honeycomb) as well.
If you’re interested in doing all this with ForceTK, take a look at this Pull Request over on GitHub.
facebooktwittergoogle_plusredditpinterestlinkedinmail

Comments

comments