2013-03-26 8 views
15

Ich erstelle eine Website für Mobiltelefone, die Größe der Fotos ändert und lädt sie.Leere Dateien hochgeladen in Android Native Browser

$('#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); 
}); 

Dies funktioniert in Chrome für Android und in Safari auf iOS, aber in dem nativen Android-Browser haben die Dateien mit einem Gehalt Länge von 0 und Namen Blob + eine UID. Wenn die Datei den formdata hinzugefügt wird, erscheint die Größe auch ziemlich groß (900k gegenüber 50k in Chrome).

Die canvasToJpegBlob Funktion:

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; 
} 

Gibt es eine Möglichkeit, diese Funktion in dem nativen Android-Browser zu bekommen?

+3

Hey, jede Chance, dass du das funktionierst, ich stecke mit genau dem gleichen Problem fest! –

+0

Nein, habe noch keine Lösung gefunden. – bjornarvh

+0

Wir haben das gleiche Problem hier - ich habe eine Prämie hinzugefügt. –

Antwort

6

Ich stieß auch auf dieses Problem und musste eine allgemeinere Lösung finden, da ich in einigen Fällen keine Kontrolle über den serverseitigen Code haben werde.

Schließlich erreichte ich eine Lösung, die fast vollständig transparent ist. Der Ansatz bestand darin, das unterbrochene FormData mit einem Blob zu füllen, der Daten in dem erforderlichen Format für multipart/form-data anhängt. Es überschreibt XHRs send() mit einer Version, die den Blob in einen Puffer liest, der in der Anfrage gesendet wird.

Hier ist der Haupt-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); 
     } 
    }; 
} 

Und es wie so verwenden, fd.append(name, value) als normal Aufruf danach:

var fd = needsFormDataShim ? new FormDataShim() : new FormData(); 
+0

Vielen Dank für das Posten dieser Lösung. Es funktioniert nicht für mich. Mein Server kann die Anforderungsdaten nicht analysieren.Und in Chrome Inspector sieht die Anforderungsnutzlast so aus (eingefügt am Anfang): '' ' ---------------------- 1e3xa79f9edc04cok0c Inhalt- Disposition: Formulardaten; Name = "Foto"; filename = "Blob" Content-Type: image/jpeg ÿØÿàJFIFÿÛC (1 #% (3 = <9387 @ H \ N @ DWE78PmQW_bghg> Mqypdx \ egcÿÛC // cB8Bcccccccccccc '' ' während normalerweise mit FormData, die Dateidaten sind ausgeblendet. FYI, ich zwinge Chrome, das Shim zu Testzwecken zu verwenden. Aber das Ergebnis ist das selbe auf Android 4.1. – jacob

+0

Ok ich habe es funktioniert, indem ich \ r \ n für statt \ n benutze. Es ist in der HTTP-Spezifikation, Wagenrücklauf zu verwenden (http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4), und mein Server (Django) benötigt es zu analysieren. Meine letzte Notiz über Chrome angezeigt Die Binärdatei-Daten noch immer auftreten, aber es ist wahrscheinlich ein Sonderfall für FormData und hat keinen Einfluss. – jacob

+0

@Jacob, froh, dass Sie es funktioniert. Ich bin bei th weg Der Moment, so kann ich die Antwort nicht aktualisieren, aber fühlen Sie sich frei, die Schaltfläche "Bearbeiten" zu drücken. Ich nehme deinen Ratschlag und benutze Wagenrückgabe in meinem eigenen Code, wenn ich zurückkomme. Prost ;-) –

1

Wie wäre es mit dem Versuch, es auf Leinwand zu zeichnen, mit Matrix, um es auf die gewünschte Größe zu skalieren und dann mit canvas.toDataURL an den Server zu senden. Überprüfen Sie diese question.

+2

Ich sehe, dass meine Frage missverstanden werden kann. Bearbeitete es jetzt, um anzugeben, dass dies für eine mobile Website und nicht für eine App gilt. – bjornarvh

+0

@bjornarvh versuchen Sie meine neue Lösung –

+0

Hat es funktioniert, indem Sie das Bild als Base64 codierte Zeichenfolge stattdessen senden. Dies als Antwort markiert, da es keine Lösung mit normalem Datei-Upload zu geben scheint. – bjornarvh

1

ich dies, um das Problem zu beheben:

// 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);