最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Empty files uploaded in Android Native browser - Stack Overflow

programmeradmin3浏览0评论

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?

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?

Share Improve this question edited May 9, 2013 at 15:50 bjornarvh asked Mar 26, 2013 at 14:04 bjornarvhbjornarvh 2332 silver badges9 bronze badges 6
  • 3 Hey, any chance you got this working, I'm stuck with the exact same issue! – Ruben Stolk Commented Apr 16, 2013 at 12:16
  • No, haven't found any solution yet. – bjornarvh Commented Apr 23, 2013 at 11:26
  • We've got the same issue here -- I've added a bounty. – Matt Hampel Commented May 4, 2013 at 0:29
  • seems no jpeg still on android? github./scottjehl/Device-Bugs/issues/33 . the larger png (900kb) is probably normal operation... – dandavis Commented May 10, 2013 at 16:32
  • 1 This is an Android bug: code.google./p/android/issues/detail?id=22441 - having the same issue myself. – Dan Smart Commented May 13, 2013 at 12:56
 |  Show 1 more ment

3 Answers 3

Reset to default 6

I also ran into this problem and needed to e 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 pletely 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();

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.

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);
发布评论

评论列表(0)

  1. 暂无评论