Epic Saga Chapter 2: Wherein I try to use an XMLHttpRequest in a Cordova app on my Windows Phone
This post is the second post in the series: Uploading Images from PhoneGap/Cordova to Azure Storage using Mobile Services
The previous chapter of this saga ended just as I realized that the super-convenient-and-easy-to-use FileTransfer plugin would not enable me to upload raw binary data to my Azure Blob storage service—multipart/form-data only. With great sadness at the prospect of having to say goodbye to such a helpful plugin, I turned to the web’s old friend XMLHttpRequest (XHR for short).
Where FileTransfer did the work of reading from a file on my phone (based on the local URI) for me, to use XHR, I would need to read the file myself into a format that XHR could send to the Blob storage service. This seemed easy enough, I figured. The Cordova File plugin has a FileReader object that would do the trick, so now my submit event handler looked like this:
// We need a FileReader to read the captured file into a byte array.
var reader = new FileReader();
…
// Handle insert
$('#add-item').submit(function (evt) {
var textbox = $('#new-item-text'),
itemText = textbox.val();
if (itemText !== '') {
var newItem = { text: itemText, complete: false };
var capturedFile;
// Do the capture before we do the insert.
// Launch device camera application, allowing user to capture an image.
navigator.device.capture.captureImage(function (mediaFiles) {
if (mediaFiles) {
// Set a reference to the captured file.
capturedFile = mediaFiles[0];
// Set the properties we need on the inserted item.
newItem.containerName = "todoitemimages";
newItem.resourceName = capturedFile.name;
alert(JSON.stringify(newItem));
}
// Do the insert and get the SAS query string from Blob storage.
todoItemTable.insert(newItem).then(function (item) {
if (item.sasQueryString !== undefined) {
// Read the file into an array buffer.
reader.readAsArrayBuffer(fileContent);
}
}, handleError).then(refreshTodoItems, handleError);
});
}
textbox.val('').focus();
evt.preventDefault();
});
This code uses the readAsArrayBuffer method to read the image into an array, which is then uploaded by the XHR. In the reader’s onload event handler (not shown) is where I created the XHR and tried to send the blob. The problem is that the onload event never got called, and every time readAsArrayBuffer returned an error. Digging into the Windows Phone source code for this plugin, I found this depressing bit of code:
public void readAsBinaryString(string options)
{
string[] optStrings = getOptionStrings(options);
string filePath = optStrings[0];
int startPos = int.Parse(optStrings[1]);
int endPos = int.Parse(optStrings[2]);
string callbackId = optStrings[3];
DispatchCommandResult(new PluginResult(
PluginResult.Status.ERROR
), callbackId);
}
First of all, what a useless error to return ERROR. Then, what I clearly failed to notice (I’ll blame the wacky state of Cordova/PhoneGap documentation) is that readAsArrayBuffer is not supported for Windows Phone. It is for iOS and Android (of course). Come on Cordova guys…please get this fixed .
OK, no biggie, I can just switch from Windows Phone to Android. After all, no one but me even has a Windows Phone, right? I mean, I don’t have an Android device that takes pictures, but there’s an Android emulator that comes with the Android SDK, right?
Stay tuned for our next painful installment… Chapter 3: Wherein I Try to Use Eclipse and the Android Emulator to Test My Cordova App
Cheers!