Memory leak when loading images - javascript

I'm using the following code to load and resize images.
$(imagesToProcess.files).each(function (idx, file) {
var img = new Image();
img.onload = function (e) {
//var resized = _resizeImage(e.target);
URL.revokeObjectURL(e.target.src);
};
img.src = URL.createObjectURL(file);
});
This code results in a gigantic memory spike in Chrome, even though I commented out the actual resizing. What am I doing wrong?

This code, which is based on this answer, solved it for me
var fileQueue = [];
var lock = false;
var img = new Image();
img.onload = function (e) {
URL.revokeObjectURL(e.target.src);
lock = false;
};
$(imagesToProcess.files).each(function (idx, file) {
fileQueue.push(file);
});
var processQueue = setInterval(processFile, 250);
function processFile() {
if (fileQueue.length == 0) {
console.log('nothing in queue');
clearInterval(processQueue);
return;
}
if (!lock) {
img.src = URL.createObjectURL(fileQueue.shift());
lock = true;
}
}

Don't use much of anonymous functions instead use reference of the functions.
$(imagesToProcess.files).each(createImage);
function createImage(idx, file){
var img = new Image();
img.onload = imageOnload;
img.src = URL.createObjectURL(file);
}
function imageOnload(e) {
URL.revokeObjectURL(e.target.src);
}
In you code for every image creation one instance of function object is created. That is what causing memory leak in your code.

I ended up solving this memory leak in my program by using an alternate method of jpeg-decoding (using library rather than browser's Image object): https://stackoverflow.com/a/62584068/2441655

Related

loop ends when function called

I'm writing what I think is some pretty routine javascript. However I'm having an issue I can't seem to resolve. I'm looping through an array and when it reaches the point of a function being called, the loop breaks. If I take the function out the loops runs to completion. I can't seem to figure it out.
for (i=0;i<response.items.length;i++) {
console.log("cover: ", decodeURIComponent(response.items[i].coverPhotoPath));
previewAlbum(decodeURIComponent(response.items[i].coverPhotoPath));
}
The code for the function being called is...
function previewAlbum(file) {
console.log("preview", file);
var galleryId = "photo";
var gallery = document.getElementById(galleryId);
var img = document.createElement("img");
img.style.cssText = 'width:200px;height:200px;';
img.src = file;
gallery.appendChild(img);
// Using FileReader to display the image content
var reader = new FileReader();
reader.onload = (function(aImg) { return function(e) { aImg.src = e.target.result; }; })(img);
reader.readAsDataURL(file);
}
Each piece of code works on it's own. But when put together the loop breaks. Any ideas?

JavaScript image.onload generates infinite loop

I've a situation where I need to work on a user-provided image with two different functions.
Get user input
Process the image and put it back processed
note: code is incomplete and shortened for brevity. Please don't point out the irrelevant.
1. Get user input
var fReader = new FileReader();
fReader.onload = function(e){
image = new Image();
image.onload = function(){
//BEGIN OF RELEVANT SECTION
processOnCanvasAndBack(image, myCallbackToProceed);
};
image.src = e.target.result;
};
fReader.readAsDataURL(src);
2. Process the image and put it back processed
function processOnCanvasAndBack(image) {
var canvas = $('<canvas></canvas>');
canvas.draw(image);
canvas.doSomeStuffLikeRotatingAndColorBalance();
//BEGIN OF RELEVANT SECTION
image.onload = function() {
myCallbackToProceed();
};
image.src = canvas.toDataURL();
}
Problem
The image.onload from 1. calls the function as expected but when I call the second image.src from 2 the first image.onload gets called again, which in turns calls 2 again and.... booooom, infinite loop (console spits too many recursions)
I tried to reset the first call with image.onload = function(){}; in various points, but it doesn't fix the issue (no more recursion, but the functions just stop being called). Right now I'm out of ideas :-(
I would suggest creating two image objects. One for the source image, and a second for the transformed / target image. You avoid mutating existing state and causing infinite loops by repeatedly setting the .src of the same image object in the onload events. Avoid mutating state whenever possible. I would also suggest using the var keyword to define the variables locally instead of in the global scope.
var fReader = new FileReader();
fReader.onload = function(e){
var sourceImage = new Image();
var targetImage = new Image();
sourceImage.onload = function() {
//BEGIN OF RELEVANT SECTION
processOnCanvasAndBack(sourceImage, targetImage, myCallbackToProceed);
}
sourceImage.src = e.target.result;
};
fReader.readAsDataURL(src);
function processOnCanvasAndBack(sourceImage, targetImage, callback) {
var canvas = $('<canvas></canvas>');
canvas.draw(sourceImage);
canvas.doSomeStuffLikeRotatingAndColorBalance();
// BEGIN OF RELEVANT SECTION
targetImage.onload = function() {
callback();
};
targetImage.src = canvas.toDataURL();
}

Javascript, load an image only if it exists

I've a series of folders containing images numbered sequentially (1.jpg, 2.jpg, 3.jpg… etc).
I'm trying to load the images with a for loop until the first non existing image is found.
I read some other article managing similar problem (check if an image exists) with onload and onerror callback functions, but I'm stuck.
Here there is some code I wrote to store the images in an array and display them in the HTML page along with their src:
var arrImages = new Array();
function loadImgSeq() {
for (var i = 0; i < 11; i++) {
arrImages[i] = new Image();
arrImages[i].src = "slides/" + i + ".jpg"
arrImages[i].width = 400;
document.body.appendChild(arrImages[i]);
document.write("<p>"+arrImages[i].src+"</p>");
}
}
PS:I've no experience in computer programming, I just do it as hobbyist.
Here's one:
var found = true;
while(found)
{
var img = new Image();
img.src = 'path';
found = img.naturalWidth? true : false;
}
do not document.write after load
do not mix append with document.write
Here is a way
var i=0,img;
function loadImgSeq() {
i++;
var img = new Image();
img.onload=function() {
document.body.appendChild(img);
loadImgSeg(); // if success, do it again - will not be called if error
}
img.src = "slides/" + i + ".jpg";// IMPORTANT, must be AFTER onload/onerror handler
}
UPDATE: If you get IE issues from cache, try
img.onload=img.complete=function() {

How do I make an image load synchronously?

I want to create an object that has an image property, but I want the contstructor to finish running only once the image is loaded. Or to describe this with code:
GraphicObject = Class.extend({
//This is the constructor
init: function(){
this.graphic = new Image();
this.graphic.src = 'path/to/file.png';
while(true)
{
this.graphic.onload = function(){break;};
//I know this won't work since the 'break' is on a different context
//but you got what I try to do.
}
}
})
For those who are unfamiliar with the Class notation I'm using in my script, it's based on this
Any ideas?
It is possible, but only with the ancient art of Base64 and Data-URL.
GIF image converted to Base64.
rune.b64
R0lGODlhIwAjAIAAAP///wAAACwAAAAAIwAjAAACf4SPqcsb3R40ocpJK7YaA35FnPdZGxg647kyqId2SQzHqdlCdgdmqcvbHXKi4AthYiGPvp9KVuoNocWLMOpUtHaS5CS54mZntiWNRWymn14tU7c2t6ukOJlKR5OiNTzQ7wb41LdnJ1coeNg3pojGqFZniPU4lTi0d4mpucmpUAAAOw==
JavaScript which loads the converted image form the same server via blocking AJAX.
loader.js
var request = new XMLHttpRequest();
var image = document.createElement('img');
request.open('GET', 'rune.b64', false);
request.send(null);
if (request.status === 200) {
image.src= 'data:image/gif;base64,' + request.responseText.trim();
document.getElementsByTagName("body")[0].appendChild(image);
}
Problems
Some older browsers don't like (big) Data-URLs
Base64 encoding makes images about 37% bigger
The whole UI is blocked till the image is loaded
This is a very-evil way
There is a non-evil way to load images in Javascript synchronously.
loadImage = async img => {
return new Promise((resolve, reject) => {
img.onload = async () => {
console.log("Image Loaded");
resolve(true);
};
});
};
Call it with await anywhere. like this
for(let i=0;i<photos.length;i++){
await loadImage(photos[i]);
}
It will load all images one by one.
Note: Calling function must be async to use await
Put the dependent code in the callback. There is no other non-evil way.
GraphicObject = Class.extend({
//This is the constructor
init: function(){
this.graphic = new Image();
this.graphic.onload = function ()
{
// the rest of the ctor code here
};
this.graphic.src = 'path/to/file.png';
}
});
var timeOut = 5*1000; //ms - waiting for max 5s to laoad
var start = new Date().getTime();
while(1)
if(img.complete || img.naturalWidth || new Date().getTime()-start>timeOut)
break;
Based on this answer.
I wrapped it into function, and it worked!
for (var i = 0, i < asset.length; i++) {
var img = new Image();
img.src = "file:///" + folder + "/" + asset[i].name;
getWdrp(img);
function getWdrp (img) {
img.onload = function(){
// work with the image file
}
}
}
This is an example that worked for me, because before, when I was processing the image without wrapping in the function, it would work async, now it is async.

Getting image dimensions using the JavaScript File API

I require to generate a thumbnail of an image in my web application. I make use of the HTML5 File API to generate the thumbnail.
I made use of the examples from Read files in JavaScript to generate the thumbnails.
I am successfully able to generate the thumbnails, but I am able to generate thumbnail only by using a static size. Is there a way to get the file dimensions from the selected file and then create the Image object?
Yes, read the file as a data URL and pass that data URL to the src of an Image: http://jsfiddle.net/pimvdb/eD2Ez/2/.
var fr = new FileReader;
fr.onload = function() { // file is loaded
var img = new Image;
img.onload = function() {
alert(img.width); // image is loaded; sizes are available
};
img.src = fr.result; // is the data URL because called with readAsDataURL
};
fr.readAsDataURL(this.files[0]); // I'm using a <input type="file"> for demonstrating
Or use an object URL: http://jsfiddle.net/8C4UB/
var url = URL.createObjectURL(this.files[0]);
var img = new Image;
img.onload = function() {
alert(img.width);
URL.revokeObjectURL(img.src);
};
img.src = url;
The existing answers helped me a lot. However, the odd order of events due to the img.onload event made things a little messy for me. So I adjusted the existing solutions and combined them with a promise-based approach.
Here is a function returning a promise with the dimensions as an object:
const getHeightAndWidthFromDataUrl = dataURL => new Promise(resolve => {
const img = new Image()
img.onload = () => {
resolve({
height: img.height,
width: img.width
})
}
img.src = dataURL
})
Here is how you could use it with an async function:
// Get a file from an input field
const file = document.querySelector('[type="file"]').files[0]
// Get the data URL of the image as a string
const fileAsDataURL = window.URL.createObjectURL(file)
// Get dimensions
const someFunction = async () => {
const dimensions = await getHeightAndWidthFromDataUrl(fileAsDataURL)
// Do something with dimensions ...
}
And here is how you could use it using the then() syntax:
// Get a file from an input field
const file = document.querySelector('[type="file"]').files[0]
// Get the data URL of the image as a string
const fileAsDataURL = window.URL.createObjectURL(file)
// Get the dimensions
getHeightAndWidthFromDataUrl(fileAsDataURL).then(dimensions => {
// Do something with dimensions
})
I have wrapped pimvdb's answer in a function for general-purpose use in my project:
function checkImageSize(image, minW, minH, maxW, maxH, cbOK, cbKO) {
// Check whether browser fully supports all File API
if (window.File && window.FileReader && window.FileList && window.Blob) {
var fr = new FileReader;
fr.onload = function() { // File is loaded
var img = new Image;
img.onload = function() { // The image is loaded; sizes are available
if(img.width < minW || img.height < minH || img.width > maxW || img.height > maxH) {
cbKO();
} else {
cbOK();
}
};
img.src = fr.result; // Is the data URL because called with readAsDataURL
};
fr.readAsDataURL(image.files[0]);
} else {
alert("Please upgrade your browser, because your current browser lacks some new features we need!");
}
}

Categories

Resources