Loading binary image data to an image fails on Android 4.1 in Cordova Project

464 views Asked by At

I have serious issues loading binary image data into a simple image-element. I coded a cordova app (using Sencha touch) which loads images the following way:

xhr.open('GET', imageUrl, true);
xhr.responseType = 'blob';

xhr.addEventListener('load', function () {
    if (xhr.status === 200) {
        // onload needed since Google Chrome doesn't support addEventListener for FileReader
        fileReader.onload = function (evt) {
            image.setSrc(evt.target.result);
        };
        // Load blob as Data URL
        fileReader.readAsDataURL(xhr.response);
    }
}, false);

On Android 5 and 4.4 (these are the ones I tested) it works like a charm. Now I ran it Android 4.1 on an ASUS Tablet and the onload callback doesn't get fired. When I throw a blob in the readAsDataURL-function, at least the onload callback is fired, but the image doesn't show any image as well :(

Has anyone a suggestion, what the failure could be or what I'm doing wrong?

1

There are 1 answers

0
The_Unknown On BEST ANSWER

Ah, finally I got it to work on Android 4.1.1 (ASUS Tablet) with the following code. Another issue was, that saving an arraybuffer response from the xhr could not simply serialized, so I converted the stuff to string. Also I receive the blob taking into account, that on some systems the Blob object simply isn't there:

function arrayBufferToString(buf) {
    return String.fromCharCode.apply(null, new Uint8Array(buf));
};

function stringToArrayBuffer(str) {
    var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
    var bufView = new Uint8Array(buf);
    for (var i=0, strLen=str.length; i<strLen; i++) {
        bufView[i] = str.charCodeAt(i);
    }
    return buf;
};

function getBlob(content, type) {
    var blob = null;

    // Android 4 only has the deprecated BlobBuilder :-(
    try {
        blob = new Blob([content], {type: type});
    } catch(e) {
        window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder ||
        window.MozBlobBuilder || window.MSBlobBuilder;

        if (window.BlobBuilder)
        {
            var bb = new BlobBuilder();
            bb.append(content);
            blob = bb.getBlob(type);
        }
    }

    return blob;
};

cachedFile = getCachedFile(...); // not of interest here, I think :-)
if (cachedFile)
{
    callback.apply(this, [window.webkitURL.createObjectURL(getBlob(stringToArrayBuffer(cachedFile.data), 'image/jpeg'))]);
}
else {
    xhr.open('GET', imageUrl, true);
    xhr.responseType = 'arraybuffer';

    xhr.addEventListener('load', function () {
        if (xhr.status === 200) {
            cachedFile = {
                url: imageUrl,
                data: arrayBufferToString(xhr.response)
            };

            addCachedFile(cachedFile); // not of interest here, I think :-)

            callback.apply(this, [window.webkitURL.createObjectURL(getBlob(xhr.response, 'image/jpeg'))]);
        }
    }, false);
    // Send XHR
    xhr.send();
}

Edit: Just did a little change and now used the Uint8Array Class instead of the Uint16Array Class because I got errors: "RangeError: byte length of Uint16Array should be a multiple of 2". Now it works well.

Edit2: Just saw, that the above code doesn't work out in all situations because of the usage of Uint8Array resp. Uint16Array.

Now I think I have a solid solution: I convert the binary responded by the image url into a base64 using canvas with the function from here http://appcropolis.com/blog/web-technology/javascript-encode-images-dataurl/ . Takes a little time, but still a working solution :)