2011-05-29 15 views
7

Tengo un problema con BlobBuilder (Chrome11) Intento obtener una imagen del servidor con solicitud XHR. Luego intento guardarlo en FS locales con BlobBuilder/FileWriter. Cada ejemplo en Internet se trata de trabajar con texto/tipo MIME simple y estos ejemplos funcionan bien. Pero cuando trato de escribir datos binarios obtenidos con XHR, el tamaño del archivo es aproximadamente 1.5-2 veces mayor que el tamaño del archivo original. Y no se puede ver en Picasa/Eye Of Gnome.BlobBuilder arruina datos binarios

var xhr = new XMLHttpRequest(); 
var photoOrigUrl = 'http://www.google.ru/images/nav_logo72.png'; 
xhr.open('GET', photoOrigUrl, true); 
xhr.onreadystatechange = function() { 
    if (xhr.readyState == 4 && xhr.status == 200) { 
     var contentType = xhr.getResponseHeader('Content-type'); 

     fsLink.root.getFile('nav_logo72.png', {'create': true}, function(fileEntry) { 
      fileEntry.createWriter(function(fileWriter) { 
       var BlobBuilderObj = new (window.BlobBuilder || window.WebKitBlobBuilder)(); 
       BlobBuilderObj.append(xhr.responseText); 

       fileWriter.write(BlobBuilderObj.getBlob(contentType)); 
      }, function(resultError) { 
       console.log('writing file to file system failed ( code ' + resultError.code + ')'); 
      }); 
     }); 
    } 
} 

xhr.send(); 

existe fsLink, esta es la extensión.

+0

Gracias por preguntar esto, ¡las respuestas han ayudado mucho a mi proyecto! – Greg

Respuesta

15

El problema es que BlobBuilder.append(xhr.responseText) está detectando su argumento como una cadena UTF-8, que es lo que devuelve XHR, y no datos binarios, que es lo que realmente es. Hay un par de trucos para obtener el BlobBuilder leerlo como datos binarios en lugar de datos de cadena:

var xhr = new XMLHttpRequest(); 
var photoOrigUrl = 'http://www.google.ru/images/nav_logo72.png'; 
xhr.open('GET', photoOrigUrl, true); 

// CHANGE 1: This stops the browser from parsing the data as UTF-8: 
xhr.overrideMimeType('text/plain; charset=x-user-defined'); 

xhr.onreadystatechange = function() { 
    if (xhr.readyState == 4 && xhr.status == 200) { 
     var contentType = xhr.getResponseHeader('Content-type'); 

     fsLink.root.getFile('nav_logo72.png', {'create': true}, function(fileEntry) { 
      fileEntry.createWriter(function(fileWriter) { 

       // CHANGE 2: convert string object into a binary object 
       var byteArray = new Uint8Array(xhr.response.length); 
       for (var i = 0; i < xhr.response.length; i++) { 
        byteArray[i] = xhr.response.charCodeAt(i) & 0xff; 
       } 

       var BlobBuilderObj = new (window.BlobBuilder || window.WebKitBlobBuilder)(); 

       // CHANGE 3: Pass the BlobBuilder an ArrayBuffer instead of a string 
       BlobBuilderObj.append(byteArray.buffer); 

       // CHANGE 4: not sure if it's needed, but keep only the necessary 
       // part of the Internet Media Type string 
       fileWriter.write(BlobBuilderObj.getBlob(contentType.split(";")[0])); 
      }, function(resultError) { 
       console.log('writing file to file system failed ( code ' + resultError.code + ')'); 
      }); 
     }); 
    } 
} 

xhr.send(); 

Esto me dio un archivo con la misma longitud que lo xhr.getResponseHeader('Content-Length') sugiere que debería haber sido.

+0

Además, recuerde aceptar respuestas cuando crea que son correctas;) – Stoive

+0

Stolve, usted es el HOMBRE !!! ¡Me has alegrado el día! ¡Muchas gracias a ti! Lo único que no entiendo es anular el tipo mimet con este "text/plain; charset = x-user-defined". Siempre he pensado que uno puede especificar el tipo de respuesta de mimet necesario aquí, pero ¿por qué deberíamos poner aquí "text/plain"? –

+0

Me alegra ayudar :). Esa línea es simplemente decirle al navegador que no haga nada inteligente con los datos que devuelve la solicitud; de lo contrario, siempre tratará de convertirlo en texto, con la codificación de texto de la página actual (creo ...). Posiblemente algo histórico, ya que XHR anteriormente solo se usaba para "text/xml" o "application/xml", supongo. – Stoive

0

Por cierto XHR2 establece una mejor manera de implementar mi tarea:

var xhr = new XMLHttpRequest(); 
xhr.open('GET', 'http://www.google.ru/images/nav_logo72.png', true); 
xhr.responseType = 'blob'; 

xhr.onreadystatechange = function() { 
    if (xhr.readyState == 4 && xhr.status == 200) { 
     // xhr.responseBlob is needed blob data 
    } 
} 

xhr.send(); 

La única decepción es que esto sigue siendo un error en Chrome: http://code.google.com/p/chromium/issues/detail?id=52486

3

Usted puede utilizar xhr.responseType='arraybuffer' sin embargo:

BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder; 

var xhr = new XMLHttpRequest(); 
xhr.open('GET', '/path/to/image.png', true); 
xhr.responseType = 'arraybuffer'; 

xhr.onload = function(e) { 
    if (this.status == 200) { 
    var bb = new BlobBuilder(); 
    bb.append(this.response); // Note: not xhr.responseText 

    var blob = bb.getBlob('image/png'); 
    ... 
    } 
}; 

xhr.send(); 
+0

+1 para un paso en la dirección correcta. Parece que BlobBuilder está en desuso, Chrome me está pidiendo que use window.Blob en su lugar. – Shanimal

+2

LOL, usted sabe esto, un enlace a la publicación de su blog http://www.html5rocks.com/en/tutorials/file/dndfiles/ – Shanimal

1

creo Stoive en el clavo pero quiero señalar que en lugar de BlobBuilder ahora hay disponibles Blob constructor que va a hacer el truco

var b = new Blob([byteArray.buffer], {'type': 'application/type'}); 

creo que esto es más de acuerdo con las normas vigentes. Muchas gracias Stoive, muy útil.

+0

+ 1.BlobBuilde ha sido duplicado ahora Blob se debe utilizar en todos los casos. – thedethfox