Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Empty files uploaded in Android Native browser

I'm creating a website for mobile phones that resizes photos and uploads them.

$('#ImgPreview canvas').each(function(pIndex) {
    vFormData.append(pIndex, canvasToJpegBlob($(this)[0]), vIssueId +'-attachment0'+ pIndex +'.jpg');
});

$.ajax({
    url: '/api/ob/issuefileupload',
    data: vFormData,
    processData: false,
    contentType: false,
    type: 'POST'
}).done(function(pData) {
    window.location = '/issue?id='+ vIssueId;
}).fail(function(pJqXHR) {
    alert(My.Strings.UploadFailed);
});

This works in Chrome for Android and in Safari on iOS, but in the native Android browser, the files have a content-length of 0 and name Blob + a UID. When the file is added to the formdata the size also seems rather large (900k opposed to 50k in Chrome).

The canvasToJpegBlob function:

function canvasToJpegBlob(pCanvas) {
    var vMimeType = "image/jpeg",
        vDataURI,
        vByteString,
        vBlob,
        vArrayBuffer,
        vUint8Array, i,
        vBlobBuilder;

    vDataURI = pCanvas.toDataURL(vMimeType, 0.8);
    vByteString = atob(vDataURI.split(',')[1]);

    vArrayBuffer = new ArrayBuffer(vByteString.length);
    vUint8Array = new Uint8Array(vArrayBuffer);
    for (i = 0; i < vByteString.length; i++) {
        vUint8Array[i] = vByteString.charCodeAt(i);
    }

    try {
        vBlob = new Blob([vUint8Array.buffer], {type : vMimeType});
    } catch(e) {
        window.BlobBuilder = window.BlobBuilder ||
                             window.WebKitBlobBuilder ||
                             window.MozBlobBuilder ||
                             window.MSBlobBuilder;

        if (e.name === 'TypeError' && window.BlobBuilder) {
            vBlobBuilder = new BlobBuilder();
            vBlobBuilder.append(vUint8Array.buffer);
            vBlob = vBlobBuilder.getBlob(vMimeType);
        } else if (e.name === 'InvalidStateError') {
            vBlob = new Blob([vUint8Array.buffer], {type : vMimeType});
        } else {
            alert(My.Strings.UnsupportedFile);
        }
    }

    return vBlob;
}

Is there any way to get this working in the native Android browser?

like image 967
bjornarvh Avatar asked Mar 26 '13 14:03

bjornarvh


3 Answers

I also ran into this problem and needed to come up with a more generic solution as in some cases I won't have control over the server-side code.

Eventually I reached a solution that is almost completely transparent. The approach was to polyfill the broken FormData with a blob that appends data in the necessary format for multipart/form-data. It overrides XHR's send() with a version that reads the blob into a buffer that gets sent in the request.

Here's the main code:

var
    // Android native browser uploads blobs as 0 bytes, so we need a test for that
    needsFormDataShim = (function () {
        var bCheck = ~navigator.userAgent.indexOf('Android')
                        && ~navigator.vendor.indexOf('Google')
                        && !~navigator.userAgent.indexOf('Chrome');

        return bCheck && navigator.userAgent.match(/AppleWebKit\/(\d+)/).pop() <= 534;
    })(),

    // Test for constructing of blobs using new Blob()
    blobConstruct = !!(function () {
        try { return new Blob(); } catch (e) {}
    })(),

    // Fallback to BlobBuilder (deprecated)
    XBlob = blobConstruct ? window.Blob : function (parts, opts) {
        var bb = new (window.BlobBuilder || window.WebKitBlobBuilder || window.MSBlobBuilder);
        parts.forEach(function (p) {
            bb.append(p);
        });

        return bb.getBlob(opts ? opts.type : undefined);
    };

function FormDataShim () {
    var
        // Store a reference to this
        o = this,

        // Data to be sent
        parts = [],

        // Boundary parameter for separating the multipart values
        boundary = Array(21).join('-') + (+new Date() * (1e16*Math.random())).toString(36),

        // Store the current XHR send method so we can safely override it
        oldSend = XMLHttpRequest.prototype.send;

    this.append = function (name, value, filename) {
        parts.push('--' + boundary + '\nContent-Disposition: form-data; name="' + name + '"');

        if (value instanceof Blob) {
            parts.push('; filename="'+ (filename || 'blob') +'"\nContent-Type: ' + value.type + '\n\n');
            parts.push(value);
        }
        else {
            parts.push('\n\n' + value);
        }
        parts.push('\n');
    };

    // Override XHR send()
    XMLHttpRequest.prototype.send = function (val) {
        var fr,
            data,
            oXHR = this;

        if (val === o) {
            // Append the final boundary string
            parts.push('--' + boundary + '--');

            // Create the blob
            data = new XBlob(parts);

            // Set up and read the blob into an array to be sent
            fr = new FileReader();
            fr.onload = function () { oldSend.call(oXHR, fr.result); };
            fr.onerror = function (err) { throw err; };
            fr.readAsArrayBuffer(data);

            // Set the multipart content type and boudary
            this.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
            XMLHttpRequest.prototype.send = oldSend;
        }
        else {
            oldSend.call(this, val);
        }
    };
}

And just use it like so, calling fd.append(name, value) as normal afterwards:

var fd = needsFormDataShim ? new FormDataShim() : new FormData();
like image 161
Andy E Avatar answered Nov 20 '22 02:11

Andy E


How about trying to draw it on canvas, using matrix to scale it to the size you wish and then sending it to server using canvas.toDataURL. Check out this question.

like image 1
Ilya Gazman Avatar answered Nov 20 '22 02:11

Ilya Gazman


i use this to fix the problem:

// not use blob, simply use key value
var form = new FormData();
// get you content type and raw data from data url
form.append( 'content_type', type);
form.append( 'content', raw);
like image 1
user3859311 Avatar answered Nov 20 '22 03:11

user3859311