Search code examples
javajavascriptcordovasencha-touchtyped-arrays

TypedArray as Cordova plugin parameter


I'm developing a plugin for Cordova 3.3.

In my javascript part I am downloading a pdf file which is stored as uInt8Array.

download: function ( ) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', 'url/to/pdf', true);
        xhr.responseType = 'arraybuffer';
        xhr.setRequestHeader('Accept', 'application/pdf');

        xhr.onload = function(e) {
            var uInt8Array = new Uint8Array(this.response); 

            window.plugins.filedownloader.saveFile(
                uInt8Array,
                "Downloads",
                function ( response ) {
                    console.log("success");
                },
                function ( response ) {
                    console.log("failure");
                }
            );
        };

        xhr.send();
    },

I commit the uInt8Array to the save method of my cordova plugin. The javascript part of my plugin looks like this:

var exec = require('cordova/exec');

function FileDownloader() {
};

FileDownloader.prototype = {
    login: function ( loginUrl, params, username, password, successCallback, failureCallback ) {
        exec( successCallback, failureCallback, "FileDownloader", "doLogin", [ loginUrl, params, username, password ] );
    },

    getHtmlPage: function ( url, successCallback, failureCallback ) {
        exec( successCallback, failureCallback, "FileDownloader", "getHtmlPage", [ url ] );
    },

    downloadAndSaveFile: function ( url, saveFolder, successCallback, failureCallback ) {
        exec( successCallback, failureCallback, "FileDownloader", "downloadAndSaveFile", [ url, saveFolder ] );
    },

    saveFile: function ( data, saveFolder, successCallback, failureCallback ) {
       exec( successCallback, failureCallback, "FileDownloader", "saveFile", [ data, saveFolder ] );  
    }
};

FileDownloader.install = function () {
    if (!window.plugins) {
        window.plugins = {}
    }

    window.plugins.filedownloader = new FileDownloader();
    return window.plugins.filedownloader;
};

cordova.addConstructor(FileDownloader.install);

The save method in the java part is the following:

public PluginResult execute(JSONArray args) {
                PluginResult result = new PluginResult(PluginResult.Status.OK, "blub");
                log("saveFile");

               try{
                   JSONObject dataValues = args.getJSONObject(0);
                } catch (JSONException e) {
                    e.printStackTrace();
                }

                return result;
            }

My problem is now, that the typedArray is no array anymore. It's a JSONObject. Indizes of the uInt8Array are now strings and not ordered anymore. Additionally there is at least a new key "byteBuffer".

Is there a way to treated a typedArray as JSONArray when handed over to the java part of the plugin or is there a different approach dealing with this problem?


Solution

  • There is a better way.

    Since I am using Sencha Touch I don't need to create the xhr object on my own. Actually, like it's done in the question, it will cause a memory problem in ios and the app will crash with larger files.

    Ext.Ajax.request() has a not documented option responseType which one can use to get an arraybuffer back.

    downloadFile: function ( record ) {
        var me = this;
        Ext.Ajax.request({
            url: record.get("url"),
            method: 'GET',
            success: function ( response ) {
                me.onFileDownloaded( response, record );
            },
            failure: function ( response ) {
            },
            scope: this,
            responseType: "arraybuffer",
            timeout: 300000
        });
    

    The response of this request is an arraybuffer, which can be used as follows:

    window.plugins.filemanager.saveUint8ByteArrayToFile(
                "Download",
                record.get("title"),
                response.responseBytes,
                response.responseBytes.byteLength,
                function ( filePath ) {
                    me.onFileSaved( filePath );
                },
                function ( message ) {
                    me.onFileSaveError( message );
                }
            );
    

    On Android the response.responseBytes will be converted into an base64 encoded String. On iOS it will be converted into NSData.