What I want to achive is using the return value of the "previewfile" function as an execution indicator for the "readfiles" function. But this needs to be after the "image.onload" part has been executed, since there I need returnThis to be set to true.
I've researched several things on Google and Stackoverflow concerning this problem and callbacks / deferred objects in general, but I cannot wrap my head around how to applicate that in this situation.
I have the following constellation in my Image uploading section:
function previewfile(file, tests, acceptedTypes, holder) {
var returnThis = false;
if (tests.filereader === true && acceptedTypes[file.type] === true) {
var reader = new FileReader();
reader.onload = function (event) {
var image = new Image();
image.onload = function() {
var testimage = new Image();
testimage.src = $(this).attr('src');
var widthOfImage = testimage.width;
var heightOfImage = testimage.height;
if (!checkImageDimensions(widthOfImage, heightOfImage)) {
// do stuff
} else {
returnThis = true;
}
};
image.src = event.target.result;
holder.appendChild(image);
};
reader.readAsDataURL(file);
} else {
// do other stuff
}
return returnThis;
}
function readfiles(files, tests, acceptedTypes, holder, progress) {
var uploadNow = previewfile(files[0], tests, acceptedTypes, holder);
if (uploadNow === true) {
// do stuff
}
} else {
// do other stuff
}
}
I would go with something like this
function readfiles(files, tests, acceptedTypes, holder, progress) {
previewfile(files[0], tests, acceptedTypes, holder, function(value){
if (uploadNow === true){
// do stuff
}
else {
// do other stuff
}
});
}
function previewfile(file, tests, acceptedTypes, holder, callback) {
...
callback(returnValue); //instead of return
}
As previewfile() relies on asynchronous activity, it is itself effectively asynchronous. As such, it can't reliably return a value, but it can return a promise.
As others have pointed out, previewfile() can be written to accept a callback, which would avoid the need for a promise. However, if you want a promise solution, here is one (certainly not the only one).
function previewfile(file, tests, acceptedTypes, holder) {
if(tests.filereader === true && acceptedTypes[file.type] === true) {
var reader = new FileReader(),
image;
var promise_a = $.Deferred(function(dfrd) {
reader.onload = function(event) {
image.attr('src', event.target.result).appendTo(holder);
dfrd.resolve();
};
reader.onerror = function() {
dfrd.reject('fileReader error');
};
}).promise();
var promise_b = $.Deferred(function(dfrd) {
image = $("<img/>").on('load', function() {
var widthOfImage = image.width;
var heightOfImage = image.height;
if (checkImageDimensions(widthOfImage, heightOfImage)) {
dfrd.resolve();
} else {
//do stuff
dfrd.reject('image loaded but dimensions did not check out');
}
}).error(function() {
dfrd.reject('image did not load');
});
}).promise();
reader.readAsDataURL(file);
return $.when(promise_a, promise_b);
} else {
// do other stuff
// Also return a promise here, even if no async is involved.
}
}
readfiles() can now be written as follows :
function readfiles(files, tests, acceptedTypes, holder, progress) {
return previewfile(files[0], tests, acceptedTypes, holder).then(function() {
// do stuff
}).then(null, function(reason) {
console.log(reason);// or display to the user in the DOM.
// do other stuff
});
}
The benefit of a promise-based solution is maybe not so much in handling success as managing errors. Note how a single handler reports several different types of error.
With the help of FelixKling and kallehj, this is the working solution (with callback):
// important
function previewfile(file, tests, acceptedTypes, holder, callback) {
var returnThis = false;
if (tests.filereader === true && acceptedTypes[file.type] === true) {
var reader = new FileReader();
reader.onload = function (event) {
var image = new Image();
image.onload = function() {
var testimage = new Image();
testimage.src = $(this).attr('src');
var widthOfImage = testimage.width;
var heightOfImage = testimage.height;
if (!checkImageDimensions(widthOfImage, heightOfImage)) {
// do stuff
} else {
returnThis = true;
}
callback(returnThis); // important
};
image.src = event.target.result;
holder.appendChild(image);
};
reader.readAsDataURL(file);
} else {
callback(returnThis); // important
}
}
function readfiles(files, tests, acceptedTypes, holder, progress) {
// important
previewfile(files[0], tests, acceptedTypes, holder, function (uploadNow) {
if (uploadNow === true) {
// do stuff
}
} else {
// do other stuff
}
}
});
Related
In an AngularJS web app, I have this block of JS code:
var _loop = function _loop(file) {
// For each file a reader (to read the base64 URL)
// and a promise (to track and merge results and errors)
var promise = new Promise(function (resolve, reject) {
var reader = new FileReader();
reader.addEventListener('load', function (event) {
var type = void 0;
var name = file.name;
// Try to find the MIME type of the file.
var match = mimeTypeMatcher.exec(file.type);
if (match) {
type = match[1]; // The first part in the MIME, "image" in image/png
} else {
type = file.type;
}
// If it's an image, try to find its size
if (type === 'image') {
var data = {
src: reader.result,
name: name,
type: type,
height: 0,
width: 0
};
var image = new Image();
image.addEventListener('error', function (error) {
reject(error);
});
image.addEventListener('load', function () {
data.height = image.height;
data.width = image.width;
resolve(data);
});
image.src = data.src;
} else if (type) {
// Not an image, but has a type
resolve({
src: reader.result,
name: name,
type: type
});
} else {
// No type found, resolve with the URL only
resolve(reader.result);
}
});
reader.addEventListener('error', function (error) {
reject(error);
});
reader.addEventListener('abort', function (error) {
reject('Aborted');
});
reader.readAsDataURL(file);
});
promises.push(promise);
};
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = files[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var file = _step.value;
_loop(file);
} } catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
and in Internet Explorer 11 in the last block:
for (var _iterator = files[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var file = _step.value;
_loop(file);
}
I get this error:
the object does not support the property or the 'jscomp_symbol_iterator0' method
I read that IE doesn't support Symbol. So, I tried to rewrite the code into:
for (var _iterator = files.length, _step; !(_iteratorNormalCompletion = (_step = _iterator[_iterator++]).done); _iteratorNormalCompletion = true) {
var file = _step.value;
_loop(file);
}
But I have this error:
Can not read property "done" of undefined
So, I know that it is not supported by IE. But is there an alternative to write that loop in another way, supported by IE?
Before changing the code, if everything works in Chrome and if you use compilation for your project, you might consider to add at the top of your entry point
import 'core-js'
At the moment core-js polyfill library is the easiest way to make Cross Browser Support
I want to upload an image file to server and then show it on browser editor on return.
For that, I have #fileInput form input (type file) to upload an image to server.
On change #fileInput, I trigger uploadAndReadURLfunction which calls app.uploader for upload.
When upload is finished, it returns to line commented "Coming here" below. However, I want it to return to the line commented "Not coming here". How can I make this happen.
var app = app || {};
(function(o) {
"use strict";
var ajax, getFormData;
ajax = function(data) {
var xmlhttp = new XMLHttpRequest(), uploaded;
xmlhttp.addEventListener('readystatechange', function() {
if(this.readyState === 4) {
if(this.status === 200) {
var res =this.response;
if(res == 1) {
console.log(res); // Coming here.
}
}
}
});
xmlhttp.open('post', o.options.processor);
xmlhttp.send(data);
};
getFormData = function(source) {
var data = new FormData(), i;
for(i = 0; i < source.files.files.length; i = i + 1) {
data.append('file[]', source.files.files[i]);
}
data.append('ajax', true);
return data;
};
o.uploader = function(options) {
o.options = options;
if(o.options.files !== undefined) {
ajax(getFormData(o.options));
}
}
}(app));
function uploadAndReadURL(input) {
if(input.files && input.files[0]) {
var f = document.getElementById('fileInput');
app.uploader({
files: f,
processor: "/geornal/image",
finished: function(data) {
console.log("burada2."); // Not coming here..
},
error: function() {
console.log('Not working');
}
});
}
}
$(document).ready(function(){
$("#icerik2").on("change", "#fileInput", function(){
uploadAndReadURL(this);
});
});
There is "finished:" section in uploadAndReadURL function. I don't
know how to call "finished" from app function.
Try calling o.options.finished() at if statement within readystatechange handler
if(res == 1) { o.options.finished(res); }
I'm trying to check if an image exists given a url using javascript, but my code is not working. Thanks!
Here's my function :
function verifyImageURL(url, callBack) {
var img = new Image();
img.src = url;
img.onload = function () {
callBack(true);
};
img.onerror = function () {
callBack(false);
};
}
And Here's how I call it:
var url = "http://greenstyle.com.br/wp-content/uploads/2012/09/Tucano-imagem-Silvia-Kochen-Wordpress.jpg";
verifyImageURL(url, function (imageExists) {
if (imageExists === true) {
alert("Image Exists");
} else {
alert("Image does not Exist");
}
});
This question hasn't had activity in a long time, but since I saw another recent answer, I thought I would share a solution which fits the asker's example pattern of using a callback, but alternatively returns a promise if no callback argument is provided:
See code in the TypeScript Playground to view types and the overloaded function signature
function loadImage (url, timeoutOrCallback, maybeCallback) {
let timeout;
let callback;
if (typeof timeoutOrCallback === 'number') {
timeout = timeoutOrCallback;
if (typeof maybeCallback === 'function') callback = maybeCallback;
}
else if (typeof timeoutOrCallback === 'function') callback = timeoutOrCallback;
const promise = callback
? undefined
: new Promise(resolve => void (callback = resolve));
const onlyRunOnce = {once: true};
let timerId = 0;
let done = false;
if (typeof timeout === 'number') {
timerId = setTimeout(() => {
done = true;
callback(false);
}, timeout);
}
const img = new Image();
img.addEventListener('load', () => {
if (done) return;
clearTimeout(timerId);
done = true;
callback(true);
}, onlyRunOnce);
img.addEventListener('error', () => {
if (done) return;
clearTimeout(timerId);
done = true;
callback(false);
}, onlyRunOnce);
img.src = url;
return promise;
}
// Usage examples:
function asyncExample (url, logId) {
const timeout = 3e3; // 3s
loadImage(url, timeout).then(imageLoaded => {
console.log(`${logId}: async with timeout:`, imageLoaded)
});
loadImage(url).then(imageLoaded => {
console.log(`${logId}: async:`, imageLoaded)
});
}
function callbackExample (url, logId) {
const timeout = 3e3; // 3s
let cb = imageLoaded => console.log(`${logId}: callback with timeout:`, imageLoaded);
loadImage(url, timeout, cb);
cb = imageLoaded => console.log(`${logId}: callback:`, imageLoaded);
loadImage(url, cb);
}
let url = 'https://i.stack.imgur.com/TxzmG.jpg';
asyncExample(url, 'SO image');
callbackExample(url, 'SO image');
url = 'https://invalid.example/image.jpg';
asyncExample(url, 'invalid image');
callbackExample(url, 'invalid image');
Try this approach
var x = new XMLHttpRequest(); x.open("get", url, true);x.onreadystatechange = function () { if (this.readyState == 4 && this.status == 200) {alert('exist');}else{alert('does not exist'};}; x.send();
I have a file drop class. User drop images on (as many as they wish) and these are then uploaded.
I call the class via from my main class:
this.fileDrop = new lx.FileDrop();
Here's the class:
(function(){
"use strict";
var FileDrop = function() {
this.init();
};
p.init = function() {
this._initEvents();
};
p._initEvents = function() {
$(window).on('drop', this.onDrop.bind(this)).on('dragover', this.onDragOver);
};
p.onDrop = function(e) {
e.preventDefault();
var self = this;
var files = e.originalEvent.dataTransfer.files;
$.each(files, function(index, file){
self.readFile(file).done(function(data) {
//how to return the data?
});
});
};
p.onDragOver = function(e) {
e.preventDefault();
};
p.readFile = function(file) {
var fileReader = new FileReader();
var deferred = $.Deferred();
fileReader.onload = function(event) {
deferred.resolve(event.target.result);
};
fileReader.onerror = function() {
deferred.reject(this);
};
fileReader.readAsDataURL(file);
return deferred.promise();
};
lx.FileDrop = FileDrop;
}(window));
My question concerns returning the image data to the main class. How can I return it? How can I store it - I wish to store everything that is returned into an array in the main class. How would this work when uploading multiple images. Would some sort of deferred work?
How about something like this:
var dfd = $.Deferred(),
images = [];
$.each(files, function(index, file){
self.readFile(file).done(function(data) {
dfd.progress(data);
images.push(data);
if(files.length === ++index)
dfd.resolve(images);
}).fail(dfd.reject);
});
Handle the deferred object where ever you like:
dfd.progress(function(file){
console.log('file successfully uploaded', file);
}).done(function(images){
console.log('all files successfully uploaded', images);
}).fail(function(){
console.log('something went wrong while uploading an image');
});
Another example:
function FileDrop(){
this.uploadCount = 0;
this.images = [];
}
FileDrop.prototype.getImages = function(){
var dfd = $.Deferred(),
size = 3,
that = this;
for(var i = 0; i < size; i++){
this.getImage(i*500, function(image){
var dummyImage = $('<img/>');
// should be 'image' from the callback in your case
that.images.push(dummyImage);
dfd.notify(dummyImage);
if(that.uploadCount === size){
dfd.resolve(that.images);
}
});
}
return dfd.promise();
};
FileDrop.prototype.getImage = function(timeout, callback){
var that = this;
setTimeout(function(){
that.uploadCount++;
callback();
}, timeout);
};
var fd = new FileDrop();
fd.getImages().progress(function(image){
console.log('progress', image);
}).done(function(imageArray){
// preferred way:
//access the array when you know it's complete in the callback
console.log('done', imageArray);
});
setTimeout(function(){
// I think this is what you asked for, however, you must make an
// assumption when the images are completed, which is a bad idea
console.log(fd.images);
}, 2000);
http://jsfiddle.net/Nm5vK/2/
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]);
};