How do I wait for FileReader onload to complete first - javascript

I am working with FileReader and I have come across an issue with the onLoad method of FileReader not being executed synchronously with the rest of the code. Basically I have 2 functions like these:
imageExists(url, callback) {
var img = new Image();
img.onload = function () { callback(true); };
img.onerror = function () { callback(false); };
img.src = url;
}
isImageCorrupt(file): boolean {
var reader = new FileReader();
var isCorrupt = false;
reader.readAsDataURL(file);
reader.onload = (e) => {
this.imageExists(e.target.result, (exists) => {
if (exists) {
isCorrupt = false;
// Image is not corrupt
} else {
isCorrupt = true;
//Image is corrupt
}
});
};
return isCorrupt;
}
The isImageCorrupt() function calls the reader.onLoad which calls the imageExists callback function, which also contains a image onload method.
The problem is that during the execution of the isImageCorrupt() function, the reader.onLoad has not changed the value of isCorrupt yet but the function has returned the value in the last line which is always false.
I want my function to wait for the reader.onLoad to finish its execution before the function returns the value.

maybe something like this?
isImageCorrupt(file): Promise<Boolean> {
return new Promise((resolve) => {
var reader = new FileReader();
reader.onload = (e) => {
var img = new Image();
img.onload = function () {
resolve(false);
};
img.onerror = function () {
resolve(true);
};
img.src = <string>e.target.result;
}
reader.readAsDataURL(file);
});
}
*disclaimer: I did not test it

You could use Promises. The code could be still refactorized using async/await
isImageCorrupt(file): Promise<boolean> {
return new Promise<boolean>((resolve,reject)=>{
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = ()=>{
var img = new Image();
img.src = reader.result;
img.onload = function () {
resolve(false);
};
img.onerror = function () {
resolve(true);
};
}
reader.onerror=()=>{reject(true)}
});
};
isImageCorrupt(yourFile).then((result)=>{/*HERE USE RESULT*/},(error)=>{HERE USE ERROR RESULT})
However you shouldn't return true o false, but resolve if it's ok and reject otherwise, whithout a boolean value in this way
isImageCorrupt(file): Promise<void> {
return new Promise<void>((resolve,reject)=>{
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = ()=>{
var img = new Image();
img.src = reader.result;
img.onload = function () {
resolve();
};
img.onerror = function () {
reject();
};
}
reader.onerror=()=>{reject()}
});
};
isImageCorrupt(yourFile).then(()=>{/*HERE OK*/},()=> {HERE
THERE WAS SOME ERROR/PROBLEM})

Related

return true in nested function part of new Image() .onload to verify image exist from url?

How can I return true when function is run inside function part of new Image() .onload, in order to verify if a url is a valid image?
var valid = false;
checkImage('https://example.com/image.png')
console.log(valid) //always false at first run
function checkImage(url) {
var image = new Image();
image.onload = function () {
if (this.width > 0) {
valid = true;
}
}
image.onerror = function() {
valid = false;
}
image.src = url;
}
I also tried setting a global variable which doesn't work,Or any other way to return true / false back via checkImage(url) ?
Got this initial solution from https://stackoverflow.com/a/55880263/8719001
(async () => {
let valid = await checkImage('https://example.com/image.png')
console.log(valid)
})();
async function checkImage(url) {
return new Promise(resolve=>{
const image = new Image()
image.onload = () => resolve(!!image.width)
image.onerror = () => resolve(false)
image.src = url
})
}
Following your code example you'll need to wrap your result in a Promise, which is an object made for "returning a result later":
function checkImage(url) {
return new Promise((resolve, reject) => {
var image = new Image();
image.onload = function () {
if (this.width > 0) {
resolve()
} else {
reject()
}
}
image.onerror = reject
image.src = url;
})
}
const valid = await checkImage('https://example.com/image.png')
Alternatively, a simpler way of doing this would be to use fetch if your only goal is to check for the file's existence (and not necessarily checking whether it works as an image):
const exists = await fetch(url, {method: 'HEAD'})
.then(response => response.status === 200)

Only allow one image at the same time with File API?

Only allow one image at the same time with File API?
What's the correct method to set the restriction for only one image at the same time?
<input id="browse" type="file" multiple>
<div id="imgs"></div>
<style type="text/css">
#imgs {
height: imageheight;
width: imagewidth;
position: absolute;
top: 39px;
left: 9px;
}
</style>
<script type="text/javascript">
window.URL = window.URL || window.webkitURL;
var elBrowse = document.getElementById("browse"),
elPic = document.getElementById("imgs"),
useBlob = false && window.URL;
function readImage(file) {
var reader = new FileReader();
reader.addEventListener("load", function() {
var image = new Image();
image.addEventListener("load", function() {
elPic.appendChild(this);
});
image.src = useBlob ? window.URL.createObjectURL(file) : reader.result;
});
reader.readAsDataURL(file);
}
elBrowse.addEventListener("change", function() {
var files = this.files; {
for (var i = 0; i < files.length; i++) {
var file = files[i];
if ((/\.(png|jpeg|jpg|gif)$/i).test(file.name)) {
readImage(file);
}
}
}
});
</script>
One solution could be a promise chain like this:
function readImage(file) {
//wrap readImage body into a promise
return new Promise((resolve) => {
var reader = new FileReader();
reader.addEventListener("load", function () {
var image = new Image();
image.addEventListener("load", function () {
elPic.appendChild(this);
//resolve the promise after the child is appended so the caller would know when to start the next one
resolve();
});
image.src = useBlob ? window.URL.createObjectURL(file) : reader.result;
});
reader.readAsDataURL(file);
});
}
elBrowse.addEventListener("change", function () {
var files = this.files; {
//start the chain
var chain = Promise.resolve();
for (var i = 0; i < files.length; i++) {
//use let to have properly scoped variable
let file = files[i];
if ((/\.(png|jpeg|jpg|gif)$/i).test(file.name)) {
//append functions to call to the chain
chain = chain.then(() => readImage(file));
}
}
}
});
The call to the next readImage() is performed after resolve() is called in the previous one - after the image is loaded and appended.
You might want to consider using loadend event also, it is emitted even when the loading fails for some reason so it won't break your chain.
https://developer.mozilla.org/en-US/docs/Web/Events/loadend
I started a new approach that leads to the desired result.
<body style="margin:8px">
<img style="position:absolute; top:39px; left:9px"
height="imageheight" width="imagewidth">
<input type="file" onchange="previewFile()">
<script type="text/javascript">
function previewFile() {
var preview = document.querySelector('img');
var file = document.querySelector('input[type=file]').files[0];
var reader = new FileReader();
reader.addEventListener("load", function () {
preview.src = reader.result;
}, false);
if ((/\.(png|jpeg|jpg|gif)$/i).test(file.name)) {
reader.readAsDataURL(file); }
}
</script>

how get value function in variable

Hello me need get variable from function
me return in answer undefined
function etr() {
var img = new Image();
img.onload = paramsImg;
img.src = picFile.result;
function paramsImg() {
return img.height;
};
};
var vvvr = etr();
alert(vvvr);
function etr() {
var img = new Image();
img.onload = paramsImg;
img.src = picFile.result;
function paramsImg() {
return img.height;
};
};
In your function, you mentioned paramsImg before its even loaded, so its not visible to img.onload.
paramsImg is declared simply as function, its not have scope outside the object. You need to use this keyword or mention fn with prototype.
function etr(picFile){
this.paramsImg = function(){
return img.height;
};
var img = new Image();
img.onload = this.paramsImg;
img.src = picFile.result;
}
picfile = {
result: 10
}
var vvvr = new etr(picfile);
alert(vvvr.paramsImg());
Your function etr doesn't return anything. I see that you are trying to return from an event handler for onload, but that only returns from the paramsImg function and not from etr (which has already returned before the image loads). You should wither make etr accept a callback function or return a Promise so that you can alert the images height after the image has loaded. Here is an example with a Promise:
function etr() {
return new Promise( ( resolve, reject ) => {
var img = new Image();
img.onload = _ => resolve(img.height);
img.onerror = reject;
img.src = picFile.result;
} );
};
var picFile = { result: 'https://dummyimage.com/600x1234/000/fff' };
(async function ( ) {
var vvvr = await etr();
alert(vvvr);
})( );

Exit function only when return is called

i have a input file which is used to upload images. However, I have to validate its size before upload. As below is the code I've tried on.
I have a parent function, that calls the method,ValidateImageSize( ):
$('input:file').change(function (e) {
if (ValidateImageSize(this))
// do something
else
alert("wrong size");
});
and the method shown as below:
var ValidateImageSize = function (input) {
var reader = new FileReader();
reader.onload = function (e) {
var img = new Image();
img.onload = function (e) {
return this.height == 40 && this.width == 40 ? true : false;
}
img.src = e.target.result;
}
reader.readAsDataURL(input.files[0]);
};
The method ValidateImageSize() always returns 'undefined' to its parents, cause it have yet executes the onload functions.
The output I need is either true/ false. Perhaps the structure of my codes itself is incorrect.
How can I solve this?
Use callback, something like as below:
var validation_callback = function(isValid) {
if (isValid) {
// do something
} else {
alert("wrong size");
}
};
$('input:file').change(function(e) {
ValidateImageSize(this, validation_callback);
});
var ValidateImageSize = function (input, callback) {
var reader = new FileReader();
reader.onload = function (e) {
var img = new Image();
img.onload = function (e) {
var isValid = this.height == 40 && this.width == 40 ? true : false;
callback(isValid);
};
img.src = e.target.result;
};
reader.readAsDataURL(input.files[0]);
};

JavaScript set value to variable defined in parent function

i want to return imageSize but it is returning undefined. i am confused i have tried many things. in the alert i am getting size.
getImageSize : function(file) {
var reader = new FileReader();
var image = new Image();
var imageSize;
reader.readAsDataURL(file);
reader.onload = function(_file) {
image.src = _file.target.result;
image.onload = function() {
imageSize = ~~(file.size/1024) +'KB';
alert(imageSize);
};
};
return imageSize;
}
Since you are loading the image asynchronously, you cannot return the size directly. The best you can do is pass in a call-back function to be called when the size is available:
getImageSize : function(file, callback) {
var reader = new FileReader();
var image = new Image();
var imageSize;
reader.readAsDataURL(file);
reader.onload = function(_file) {
image.src = _file.target.result;
image.onload = function() {
imageSize = ~~(file.size/1024) +'KB';
callback(imageSize);
};
};
}

Categories

Resources