javascript - data - La actualización rápida de la imagen con URI de datos provoca el almacenamiento en caché y la pérdida de memoria
image url to base64 (8)
A menos que Safari o Mobile Safari no filtren URL de datos, el lado del servidor podría ser la única forma de hacerlo en todos los navegadores.
Probablemente, lo más sencillo sería hacer una URL para su flujo de imágenes, GET
ting da una respuesta 302 o 303 a una URL de un solo uso que proporcionará la imagen deseada. Probablemente tendrás que destruir y volver a crear las etiquetas de imagen para forzar una recarga de la URL.
También estará a merced del navegador con respecto a su comportamiento de almacenamiento en caché de img
. Y la misericordia de mi comprensión (o falta de comprensión) de la especificación HTTP. Aún así, a menos que la operación del lado del servidor no se ajuste a sus requisitos, intente esto primero . Se agrega a la complejidad del servidor, pero este enfoque utiliza el navegador de forma mucho más natural.
Pero ¿qué hay de usar el navegador de forma poco natural? Dependiendo de cómo los navegadores implementan los iframe
y manejan su contenido asociado, es posible que pueda hacer que las direcciones URL de datos funcionen sin perder la memoria. Esto es una especie de mierda de Frankenstein y es exactamente el tipo de tonterías que nadie debería tener que hacer. Al revés: podría funcionar. Desventaja: hay muchas maneras de probarlo y un comportamiento irregular e indocumentado es exactamente lo que yo esperaba.
Una idea: incrustar un iframe
contiene una página; esta página y la página en la que está incrustada en el uso de mensajes cruzados de documentos (tenga en cuenta el VERDE en la matriz de compatibilidad); embeddee obtiene la cadena PNG y la pasa a la página incrustada, que luego crea una etiqueta img
adecuada. Cuando el usuario debe mostrar un nuevo mensaje, destruye el iframe
incrustado (con suerte liberando la memoria de la url de datos), crea uno nuevo y le pasa la nueva cadena PNG.
Si desea ser un poco más inteligente, en realidad podría incrustar la fuente del marco incrustado en la página de inserción como una url de datos; sin embargo, esto podría filtrar la url de datos, lo que supongo que sería una justicia poética si se intentara semejante alcance.
"Algo que funcione en Safari sería mejor". La tecnología del navegador sigue avanzando, de manera desigual. Cuando no te entregan la funcionalidad en un plato, debes desviarte.
Tengo una página web que transmite rápidamente JSON desde el servidor y muestra partes del mismo, aproximadamente 10 veces / segundo. Una parte es una imagen PNG codificada en base64. He encontrado algunas formas diferentes de mostrar la imagen, pero todas causan un uso ilimitado de la memoria. Se eleva de 50mb a 2gb en minutos. Sucede con Chrome, Safari y Firefox. No he probado IE.
Descubrí el uso de la memoria primero mirando Activity Monitor.app: el proceso de Google Chrome Renderer consume memoria continuamente. Luego, miré el inspector de recursos de Chrome ( View
> Developer
> Developer Tools
, Resources
), y vi que estaba guardando las imágenes en caché . Cada vez que cambié el img src
, o creé una nueva Image () y configuré su src
, Chrome lo guardó en caché. Solo puedo imaginar que los otros navegadores están haciendo lo mismo.
¿Hay alguna manera de controlar este almacenamiento en caché? ¿Puedo apagarlo o hacer algo furtivo para que nunca suceda?
Edición: me gustaría poder usar la técnica en Safari / Mobile Safari. Además, estoy abierto a otros métodos para actualizar rápidamente una imagen si alguien tiene alguna idea.
Estos son los métodos que he probado. Cada uno reside en una función que se llama al completar AJAX.
Método 1: establecer directamente el atributo src
en una etiqueta img
Rápido. Muestra muy bien. Fugas como locas.
$(''#placeholder_img'').attr(''src'', ''data:image/png;base64,'' + imgString);
Método 2: reemplace img
con un canvas
y use drawImage
Muestra bien, pero todavía gotea.
var canvas = document.getElementById("placeholder_canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0);
}
img.src = "data:image/png;base64," + imgString;
Método 3 - Convertir a binario y reemplazar contenido de canvas
Estoy haciendo algo mal aquí: las imágenes se muestran pequeñas y parecen un ruido aleatorio. Este método utiliza una cantidad controlada de memoria (crece hasta 100 mb y se detiene), pero es lento, especialmente en Safari (~ 50% de uso de CPU allí, 17% en Chrome). La idea surgió de esta pregunta similar de SO: Fuga de URI de datos en Safari (era: Pérdida de memoria con lienzo HTML5)
var img = atob(imgString);
var binimg = [];
for(var i = 0; i < img.length; i++) {
binimg.push(img.charCodeAt(i));
}
var bytearray = new Uint8Array(binimg);
// Grab the existing image from canvas
var ctx = document.getElementById("placeholder_canvas").getContext("2d");
var width = ctx.canvas.width,
height = ctx.canvas.height;
var imgdata = ctx.getImageData(0, 0, width, height);
// Overwrite it with new data
for(var i = 8, len = imgdata.data.length; i < len; i++) {
imgdata.data[i-8] = bytearray[i];
}
// Write it back
ctx.putImageData(imgdata, 0, 0);
He utilizado diferentes métodos para resolver este problema, ninguno de ellos funciona. Parece que la memoria se pierde cuando img.src = base64string y esas memorias nunca pueden liberarse. Aquí está mi solución.
// Destroy old image
if(temporaryImage) objectURL.revokeObjectURL(temporaryImage);
// Create a new image from binary data
var imageDataBlob = convertDataURIToBlob(base64Image);
// Create a new object URL object
temporaryImage = objectURL.createObjectURL(imageDataBlob);
// Set the new image
imageElement.src = temporaryImage;
Tenga en cuenta que debe convertir base64 a jpeg buffer.
Mi aplicación Electron se actualiza img cada 50 ms, y la memoria no se pierde. Olvídate del uso del disco. La gestión de la memoria de Chrome me molesta.
Intente configurar image.src = "" después de dibujar.
var canvas = document.getElementById("placeholder_canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0);
//after drawing set src empty
img.src = "";
}
img.src = "data:image/png;base64," + imgString;
Esto podría ser ayudado
No creo que exista ninguna garantía sobre el uso de memoria de las URL de datos. Si puede encontrar una manera de lograr que se comporten en un navegador, esto garantiza poco o nada sobre otros navegadores o versiones.
Si coloca los datos de su imagen en un blob y luego crea una URL de blob, puede desasignar esos datos.
Aquí hay un ejemplo que convierte un URI de datos en una URL de blob; Es posible que deba cambiar / eliminar los prefijos webkit-
& WebKit-
en otros navegadores que no sean Chrome y posiblemente versiones futuras de Chrome.
var parts = dataURL.match(/data:([^;]*)(;base64)?,([0-9A-Za-z+/]+)/);
//assume base64 encoding
var binStr = atob(parts[3]);
//might be able to replace the following lines with just
// var view = new Uint8Array(binStr);
//haven''t tested.
//convert to binary in ArrayBuffer
var buf = new ArrayBuffer(binStr.length);
var view = new Uint8Array(buf);
for(var i = 0; i < view.length; i++)
view[i] = binStr.charCodeAt(i);
//end of the possibly unnecessary lines
var builder = new WebKitBlobBuilder();
builder.append(buf);
//create blob with mime type, create URL for it
var URL = webkitURL.createObjectURL(builder.getBlob(parts[1]))
return URL;
La desasignación es tan fácil como:
webkitURL.revokeObjectURL(URL);
Y puede usar su URL de blob como img
su img
.
Desafortunadamente, las URL de blob no parecen ser compatibles con IE antes de la v10.
Referencia API:
http://www.w3.org/TR/FileAPI/#dfn-createObjectURL
http://www.w3.org/TR/FileAPI/#dfn-revokeObjectURL
Referencia de compatibilidad:
Sé que han pasado años desde que se publicó este problema, pero el problema sigue existiendo en las versiones recientes de Safari Browser. Así que tengo una solución definitiva que funciona en todos los navegadores, y creo que esto podría salvar trabajos o vidas.
Copie el siguiente código en algún lugar de su página html:
<script src=''js/stringview.js''></script>
Luego, cuando reciba un nuevo fotograma / imagen base64Image
en formato base64 (por ejemplo, data:image/jpeg;base64, LzlqLzRBQ...
) y desee actualizar un objeto html <img />
imageElement
, use este código:
window.URL = window.URL ||
window.webkitURL;
function blobify_dataurl(dataURL){
var parts = dataURL.match(/data:([^;]*)(;base64)?,([0-9A-Za-z+/]+)/);
//assume base64 encoding
var binStr = atob(parts[3]);
//convert to binary in StringView
var view = StringView.base64ToBytes(parts[3]);
var blob = new Blob([view], {type: parts[1]}); // pass a useful mime type here
//create blob with mime type, create URL for it
var outURL = URL.createObjectURL(blob);
return outURL;
}
Repita este último código tanto como sea necesario y no aparecerá ninguna pérdida de memoria. Esta solución no requiere el uso del elemento de lienzo, pero puede adaptar el código para que funcione.
Tuve un problema muy similar.
Configurando img.src a dataUrl Leaks Memory
En pocas palabras, simplemente trabajé alrededor del elemento Imagen. Utilizo un decodificador javascript para decodificar y mostrar los datos de la imagen en un lienzo. A menos que el usuario intente descargar la imagen, tampoco sabrán la diferencia. El otro inconveniente es que vas a estar limitado a los navegadores modernos. El lado positivo es que este método no se filtra como un tamiz :)
parcheando la respuesta de ellisbben, ya que BlobBuilder está obsoleto y https://developer.mozilla.org/en-US/Add-ons/Code_snippets/StringView proporciona lo que parece ser una conversión rápida y agradable de base64 a UInt8Array:
en html:
URL.revokeObjectURL(outURL);
en js:
fs.writeFile(''img0.jpg'', img_data, function (err) {
// console.log("save img!" );
});
document.getElementById("my-img").src = ''img0.jpg?''+img_step;
img_step+=1;
Todavía no lo veo actualizando la imagen en Safari móvil, pero Chrome puede recibir datos de datos de forma rápida en websocket y mantenerse al día mucho mejor que tener que iterar manualmente sobre la cadena. Y si sabe que siempre tendrá el mismo tipo de dataurl, incluso podría cambiar el regex por una subcadena (¿probablemente más rápido ...?)
Al ejecutar algunos perfiles de memoria rápida, parece que Chrome incluso puede mantenerse al día con las desasignaciones (si recuerdas hacerlo ...):
// Methods to address the memory leaks problems in Safari
var BASE64_MARKER = '';base64,'';
var temporaryImage;
var objectURL = window.URL || window.webkitURL;
function convertDataURIToBlob(dataURI) {
// Validate input data
if(!dataURI) return;
// Convert image (in base64) to binary data
var base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
var base64 = dataURI.substring(base64Index);
var raw = window.atob(base64);
var rawLength = raw.length;
var array = new Uint8Array(new ArrayBuffer(rawLength));
for(i = 0; i < rawLength; i++) {
array[i] = raw.charCodeAt(i);
}
// Create and return a new blob object using binary data
return new Blob([array], {type: "image/jpeg"});
}
var inc = 1;
var Bulk = 540;
var tot = 540;
var audtot = 35.90;
var canvas = document.getElementById("myCanvas");
//var imggg = document.getElementById("myimg");
canvas.width = 550;
canvas.height = 400;
var context = canvas.getContext("2d");
var variation = 0.2;
var interval = 65;
function JLoop() {
if (inc < tot) {
if (vid.currentTime < ((audtot * inc) / tot) - variation || (vid.currentTime > ((audtot * inc) / tot) + variation)) {
contflag = 1;
vid.currentTime = ((audtot * inc) / tot);
}
// Draw the animation
try {
context.clearRect(0, 0, canvas.width, canvas.height);
if (arr[inc - 1] != undefined) {
context.drawImage(arr[inc - 1], 0, 0, canvas.width, canvas.height);
arr [inc - 1] .src = "";
//document.getElementById("myimg" + inc).style.display = "block";;
// document.getElementById("myimg" + (inc-1)).style.display = "none";
//imggg.src = arr[inc - 1].src;
}
$("#audiofile").val(inc);
// clearInterval(ref);
} catch (e) {
}
inc++;
// interval = 60;
//setTimeout(JLoop, interval);
}
else {
}
}
var ref = setInterval(JLoop, interval);
});
Trabajó para mí en la pérdida de memoria gracias tio.