readasdataurl - Alto uso de memoria de JavaScript con FileReader, imágenes y<canvas>
read file javascript (2)
Estoy trabajando en la carga múltiple de imágenes, eso suena bien, pero ... como siempre, problema de memoria.
El objetivo del script es sobrevivir al cargar más de 100 imágenes (300Mb +). Entonces, si encuentra (todavía estoy javascript lame) algún problema, por favor, avíseme. Gracias.
Mi código:
CFileReader.prototype.proccessFile = function(cb) {
// this means File
var reader = new FileReader();
reader.readAsDataURL(this);
reader.onload = (function (f) {
return function(e) {
var image = new Image();
image.src = e.target.result;
image.onload = (function(f) {
return function() {
var maxWidth = 700,
maxHeight = 700,
imageWidth = this.width,
imageHeight = this.height;
if (imageWidth > imageHeight) {
if (imageWidth > maxWidth) {
imageHeight *= maxWidth / imageWidth;
imageWidth = maxWidth;
}
}
else {
if (imageHeight > maxHeight) {
imageWidth *= maxHeight / imageHeight;
imageHeight = maxHeight;
}
}
var canvas = document.createElement(''canvas'');
canvas.width = imageWidth;
canvas.height = imageHeight;
var ctx = canvas.getContext("2d");
ctx.drawImage(this, 0, 0, imageWidth, imageHeight);
if(typeof cb == ''function'') {
cb(f,canvas.toDataURL());
}
delete canvas;
delete ctx;
return;
}
})(f);
};
})(this);
}
Creo que window.createObjectURL es más rápido que FileReader, debe usar ambos con Fall back para Filereader ... también puede verificar el rendimiento de cada operación porque hay diferencias por navegador, por ejemplo http://jsperf.com/canvas-image- cambio de tamaño y no te olvides de revocarObjectURL por razones de memoria ... también podrías utilizar webworkers http://www.html5rocks.com/en/tutorials/file/filesystem-sync/#toc-readingsync
if("createObjectURL" in window || "URL" in window &&
"createObjectURL" in window.URL || "webkitURL" in window &&
"createObjectURL" in window.webkitURL) {
if("createObjectURL" in window) {
// Chrome exposes create/revokeObjectURL directly on window
objURL = window.createObjectURL(file);
} else if("webkitURL" in window) {
// Chrome exposes create/revokeObjectURL on the new webkitURL API
objURL = window.webkitURL.createObjectURL(file);
} else {
// FF4 exposes create/revokeObjectURL on the new URL API
objURL = window.URL.createObjectURL(file);
}
// RESIZE image
// STORED IN
// objURL
} else {
// fallback to FileReader for FF3.6
reader = new FileReader();
reader.onload = function(event) {
// RESIZE image
// STORED IN
// event.target.result
}
reader.onprogress = function (evt) {
if (evt.lengthComputable) {
var percentLoaded = Math.round((evt.loaded / evt.total) * 100);
console.log(percentLoaded);
}
}
reader.readAsDataURL(file);
}
Ok, algunos días de googlear, muchas horas para encontrar la mejor solución. (No es el mejor de todos, pero el mejor, que puedo descifrar). Si alguien tiene algo mejor, coloca su respuesta o simplemente da mala reputación.
Objetivo uno: almacenar todo en el espacio global (lienzo, lector, contexto de lienzo, imagen)
window.reader = new FileReader();
window.canvas = document.createElement(''canvas'');
window.image = new Image();
window.ctx = window.canvas.getContext("2d");
Objetivo dos: Usar el método canvas.toBlob (implementado solo en Firefox en este momento, necesitarás algunos polyfill para eso)
Objetivo tres: Agregar algunas esperas. Esto no puedo explicarlo de manera determinista, solo es empírico. El objetivo es, para permitir el navegador, llamar al recolector de basura marque entre llamadas.
Entonces, está mi último script dev:
window.prepareFile = function (index,cb) {
// this means file html element
var res = window.configuration.photo.resolution.split(":");
//var res = [300,200];
var reader = window.reader;
$(''#fm-up-status > tr[for="''+this.files[index].name+''"]'').children(''td'')[3].innerHTML = ''Zpracovávám'';
reader.readAsDataURL(this.files[index]);
reader.onload = (function (f) {
return function(e) {
setTimeout(function(){
var image = window.image;
image.src = e.target.result;
image.onload = (function(f) {
return function() {
var maxWidth = parseInt(res[0]),
maxHeight = parseInt(res[1]),
imageWidth = this.width,
imageHeight = this.height;
if (imageWidth > imageHeight) {
if (imageWidth > maxWidth) {
imageHeight *= maxWidth / imageWidth;
imageWidth = maxWidth;
}
}
else {
if (imageHeight > maxHeight) {
imageWidth *= maxHeight / imageHeight;
imageHeight = maxHeight;
}
}
var canvas = window.canvas;
canvas.width =0;
canvas.width = imageWidth;
canvas.height = imageHeight;
window.ctx.drawImage(this, 0, 0, imageWidth, imageHeight);
canvas.toBlob(
function (blob) {
var formData = new FormData();
formData.append(''file'', blob, f.name);
cb(f.name,formData);
},
''image/jpeg''
);
return;
}
})(f);
return;
},500);
};
})(this.files[index]);
return;
}
Luego puede cargar datos de forma normal (multipar) de esta manera:
window.uploader = function(filename,formdata,cb) {
$(''#fm-up-status > tr[for="''+filename+''"]'').children(''td'')[3].innerHTML = ''Uploaduji'';
xhr = jQuery.ajaxSettings.xhr();
if (xhr.upload) {
xhr.upload.addEventListener(''progress'', function (evt) {
if (evt.lengthComputable) {
var floatComplete = evt.loaded / evt.total;
window.uploadProgress(filename,floatComplete);
}
}, false);
}
provider = function () {
return xhr;
};
$.ajax({
url: window.uploadUri,
type: "POST",
data: formdata,
xhr: provider,
processData: false,
contentType: false,
dataType: "json",
success: function (res,state,xhr) {
cb(false,res,xhr);
return;
},
error: function (xhr, state, err) {
cb(err,false,xhr)
return;
}
});
};
Este código realmente no está listo para producción, es una simple prueba de concepto