Convierta el URI de datos en un archivo y luego agréguelo a FormData
He estado intentando volver a implementar un cargador de imágenes HTML5 como el del sitio Mozilla Hacks, pero funciona con los navegadores WebKit. Parte de la tarea consiste en extraer un archivo de imagen del canvas
objeto y adjuntarlo a un objeto FormData para cargarlo.
El problema es que si bien canvas
tiene la toDataURL
función de devolver una representación del archivo de imagen, el objeto FormData solo acepta objetos File o Blob de File API .
La solución Mozilla utilizó la siguiente función exclusiva de Firefox en canvas
:
var file = canvas.mozGetAsFile("foo.png");
...que no está disponible en los navegadores WebKit. La mejor solución que se me ocurre es encontrar alguna forma de convertir un URI de datos en un objeto Archivo, que pensé que podría ser parte de la API de archivos, pero no puedo encontrar algo para hacer eso.
¿Es posible? Si no, ¿alguna alternativa?
Después de jugar con algunas cosas, logré resolverlo por mí mismo.
En primer lugar, esto convertirá un dataURI en un Blob:
function dataURItoBlob(dataURI) {
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = atob(dataURI.split(',')[1]);
else
byteString = unescape(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
}
A partir de ahí, es fácil agregar los datos a un formulario para que se carguen como un archivo:
var dataURL = canvas.toDataURL('image/jpeg', 0.5);
var blob = dataURItoBlob(dataURL);
var fd = new FormData(document.forms[0]);
fd.append("canvasImage", blob);
BlobBuilder y ArrayBuffer ahora están en desuso, aquí está el código del comentario principal actualizado con Blob constructor:
function dataURItoBlob(dataURI) {
var binary = atob(dataURI.split(',')[1]);
var array = [];
for(var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {type: 'image/jpeg'});
}
Este funciona en iOS y Safari.
Necesita usar la solución ArrayBuffer de Stoive pero no puede usar BlobBuilder, como indica vava720, así que aquí está la combinación de ambos.
function dataURItoBlob(dataURI) {
var byteString = atob(dataURI.split(',')[1]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], { type: 'image/jpeg' });
}
Firefox tiene los métodos canvas.toBlob() y canvas.mozGetAsFile() .
Pero otros navegadores no.
Podemos obtener la URL de datos del lienzo y luego convertir la URL de datos en un objeto blob.
Aquí está mi dataURLtoBlob()
función. Es muy corto.
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {type:mime});
}
Utilice esta función con FormData para manejar su lienzo o URL de datos.
Por ejemplo:
var dataurl = canvas.toDataURL('image/jpeg',0.8);
var blob = dataURLtoBlob(dataurl);
var fd = new FormData();
fd.append("myFile", blob, "thumb.jpg");
Además, puede crear un HTMLCanvasElement.prototype.toBlob
método para un navegador que no sea del motor gecko.
if(!HTMLCanvasElement.prototype.toBlob){
HTMLCanvasElement.prototype.toBlob = function(callback, type, encoderOptions){
var dataurl = this.toDataURL(type, encoderOptions);
var bstr = atob(dataurl.split(',')[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
var blob = new Blob([u8arr], {type: type});
callback.call(this, blob);
};
}
Ahora canvas.toBlob()
funciona para todos los navegadores modernos, no solo para Firefox. Por ejemplo:
canvas.toBlob(
function(blob){
var fd = new FormData();
fd.append("myFile", blob, "thumb.jpg");
//continue do something...
},
'image/jpeg',
0.8
);