How to I refer to the `postMessage()` function from my XHR callbacks? - javascript

The file uploads work perfectly, I can't get the progess and load events to callback
I have the following in a JavaScript file as my WebWorker code:
UploadFileWorker.js
function uploadFile(url, m) {
var f = m.file;
var fd = new FormData();
fd.append('file', f, f.name);
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', function (e) {
m.set('status', (e.loaded / e.total) * 100 );
postMessage(m); // <-- never makes the call, doesn't throw and error either
});
xhr.upload.addEventListener('load', function (e) {
m.set('status',100);
postMessage(m); // <-- never makes the call, doesn't throw and error either
});
xhr.open('POST', url, true);
xhr.send(fd);
}
function getUploadURL(m) {
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function () {
var url = this.responseText;
uploadFile(url,m);
});
xhr.open('GET', '/upload', false);
xhr.send();
}
var q = [];
onmessage = function(e) {
postMessage(e.data);
q.push(e.data);
while (q.length > 0) {
var m = q.pop();
getUploadURL(m);
}
};
The postMessage(e.data); in the onmessage function work perfectly.
The postMessage(m); in the xhr.upload.addEventListeners() callbacks never happen. They worked fine before I tried and move these functions into WebWorker code.
Is this a scope issue? If so, how do I prefix the calls to get them to work.?
For completeness here is how I am defining my instance of Worker and kicking it off.
onButtonClick: function(button, e, eOpts) {
var ugv = this.getUploadGridView();
var fs = this.getFileUploadStore();
var worker = new Worker('/js/UploadFileWorker.js');
worker.onmessage = function(e) {
console.log(e);
};
var selected = ugv.getSelectionModel().getSelection();
Ext.each(selected, function(m) {
var o = {};
o.name = m.get('name');
o.size = m.get('size');
o.status = m.get('status');
o.file = m.file;
worker.postMessage(o);
});
},
Again the actual uploading of the files works great, I can't figure out how to call postMessage(); from inside the xhr callbacks.

This is apparently a bug that has been fixed just recently.
Issue 71433002: Enable XHR upload progress events for Workers. (Closed)
Workaround until Chrome gets updated
xhr.addEventListener(
instead of
xhr.upload.addEventListener(
This has the drawback that progress only gets called for every 1MB, files smaller than 1MB never get a progress event fired.

Related

how many times does 'progressEvents' take place during ajax file uploading?

I was new with ajax uploading , i want to add a progress bar during the uploading process.
i add a register function of progressEvent , but turns out the function only execute one time.so my progress bar will not usefull .
below is my code ,what 's wrong with my code ? what should i do the right way?thanks!
var fileEle = document.querySelector('#file');
file.onchange = function(e){
let file = e.target.files[0];
var formData = new FormData();
formData.append('book',file);
var xhr = new XMLHttpRequest();
xhr.onprogress = function(e){
console.log(e);//this only execute once ,why?
}
xhr.open('post','http://127.0.0.1:8080/profile');
xhr.send(formData);
}
in fact , the progressEvent will emit for many times.
you should register the xhr.upload object
below is the correct code:
var fileEle = document.querySelector('#file');
file.onchange = function(e){
let file = e.target.files[0];
var formData = new FormData();
formData.append('book',file);
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function(e){
console.log(e);//this only execute once ,why?
}
xhr.open('post','http://127.0.0.1:8080/profile');
xhr.send(formData);
}

Add progress bar to lightbox2

I was searching a way to add a progress indicator to lightbox2 script. My JS is pretty poor and I need a hint on where to start.
I assume I need to rewrite Image class prototype, to add methods like onprogress. This is well described here
But when I add those methods at the start of the script, they don't operate at all. I tried inserting console.log() to one of them, nothing logged, they just don't execute.
See comments in code below.
What exactly am I doing wrong, please?
//start of the original lightbox.js
//this is the code I've inserted, you can see I've added
//multiple console.log()s here just to check if methods called
Image.prototype.load = function(url){
console.log('1');
var thisImg = this;
var xmlHTTP = new XMLHttpRequest();
xmlHTTP.open('GET', url,true);
xmlHTTP.responseType = 'arraybuffer';
xmlHTTP.onload = function(e) {
var blob = new Blob([this.response]);
thisImg.src = window.URL.createObjectURL(blob);
console.log('2');
};
xmlHTTP.onprogress = function(e) {
thisImg.completedPercentage = parseInt((e.loaded / e.total) * 100);
console.log('3');
};
xmlHTTP.onloadstart = function() {
thisImg.completedPercentage = 0;
console.log('4');
};
xmlHTTP.send();
console.log('5');
};
Image.prototype.completedPercentage = 0;
//original script continues from here
....
//here imgPreloader declared, I assume it inherits methods from
//rewritten Image's prototype above
var imgPreloader = new Image();
imgPreloader.onload = (function(){
this.lightboxImage.src = this.imageArray[this.activeImage][0];
this.resizeImageContainer(imgPreloader.width, imgPreloader.height);
}).bind(this);
//preloader's src changes and his methods should execute here
//but they don't
imgPreloader.src = this.imageArray[this.activeImage][0];

xhr response with for loop not working

i have xhr with for loop and it work very rare
for(var i = 0; i < this.files.length; i++) {
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function(e) {
};
xhr.onreadystatechange = function(e) {
if(this.readyState === 4) {
console.log(xhr.responseText);
}
};
var formdata = new FormData();
formdata.append("files", this.files[i]);
console.log(this.files[i]);
xhr.open('POST', 'slike.php');
xhr.send(formdata);
}
I'm calling this slike.php. And it work very well, but on responseText, it's not good, sometimes get only last file from loop, sometimes get two file(with same text). I don't know how ti fix it, i was looking everywhere, and can't found the answer.
XHR are asynchronous by default, so unless you specify otherwise (async = false in XHR open() method), your loop may have finished before the first XHR may have been initialized.
But the i in your code (this.files[i]) in the loop refers to the same i of your loop, so i may be assigned this.files.length-1 when the first XHR begins. Taht is why you always get only the last file.
That is why you have to create what is called a closure to make sure the index you are using is the one you really want to use.
Try this :
for (var i = 0; i < this.files.length; i++) {
(function(index, files) { // In this closure : parameters of a function in JS
// are available only in the function,
// and cannot be changed from outside of it
var xhr = new XMLHttpRequest(); // variables declared in a function in JS
// are available only inside the function
// and cannot be changed from outside of it
xhr.upload.onprogress = function (e) {
};
xhr.onreadystatechange = function (e) {
if (this.readyState === 4) {
console.log(xhr.responseText);
}
};
var formdata = new FormData();
formdata.append("files", files[index]); // `index` has nothing to do with `i`, now:
// if `i` changes outside of the function,
//`index` will not
console.log(files[index]); // Don't keep `console.log()` in production code ;-)
xhr.open('POST', 'slike.php');
xhr.send(formdata);
})(i, this.files)
}
Or if would want to really get the files sequentially :
var i = 0,
fileNb = this.files.length;
function getNextFile(file) {
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function (e) {
};
xhr.onreadystatechange = function (e) {
if (this.readyState === 4) {
console.log(xhr.responseText);
if (++i < fileNb) getNextFile(this.files[i]);
}
};
var formdata = new FormData();
formdata.append("files", file);
console.log(file); // Don't keep `console.log()` in production code ;-)
xhr.open('POST', 'slike.php');
xhr.send(formdata);
}
getNextFile(i);
console.log(xhr.responseText);
You are accessing the current value of xhr (which will usually be the last one created) there instead of the object to which the event handler is attached.
Use this instead of xhr as you did in the previous line.

XMLHttpRequest on load in saveback to calling object

I'm trying to load in many json files for a HTML5 game that will serve as sprite sheets. Previously I've did this synchronously but my new goal is to do this asynchronously.
I have run into a problem though where I'm trying to saving back to the calling object. This is so the information loaded can be used later and so a flag (loaded) can be set so the system knows when a resource has been loaded. Below is my XMLHttpRequest code. I have substituted "spritesheet" for what ever the call should be to save back to the parent.
function SpriteSheet(filename)
{
var tmpFileName = "json/" + filename;
this.loaded = false;
var xhr = new XMLHttpRequest();
xhr.open("GET",tmpFileName,true);
xhr.onload = function(event){
var parsed = JSON.parse(this.responseText);
"spritesheet".img=new Image();
"spritesheet".img.src = "imgs/" + parsed["imgLoc"];
"spritesheet".animations = parsed["animations"];
"spritesheet".sprites = parsed["sprites"];
"spritesheet".loaded = true;
};
xhr.send();
}
Can somebody inform me how I can save back to the the parent or if this is completely the wrong approach can they point me in the direction of a solution.
I found that by creating a var in the 'class' that is a reference to the object and using it in the onload function works, for example:
function SpriteSheet(filename)
{
var tmpFileName = "json/" + filename;
this.loaded = false;
var caller = this;
var xhr = new XMLHttpRequest();
xhr.open("GET",tmpFileName,true);
xhr.onload = function(event){
var parsed = JSON.parse(this.responseText);
caller.img=new Image();
caller.img.src = "imgs/" + parsed["imgLoc"];
caller.animations = parsed["animations"];
caller.sprites = parsed["sprites"];
caller.loaded = true;
};
xhr.send();
}

Access events of XHR2 in Collection

I have a Collection that sends files successfully to the server with XMLHttpRequest.
But I cannot figure out how to attach functions to the XHR2 events.
It only seems to be working when the code is directly inside of send():
var Photos = Backbone.Collection.extend({
url: config.url,
/**
* Send file to server.
* #todo Should Backbone.sync be overwritten instead?
*/
send: function (file) {
var data = new FormData(),
xhr = new XMLHttpRequest();
// ======> Doesn't work:
xhr.addEventListener('load', this.onLoad(xhr));
// ======> Doesn't work either:
xhr.onload = this.onLoad(xhr);
// ======> But this works:
xhr.onload = function () {
var response = $.parseJSON(xhr.responseText);
console.log(response); // Works!
};
data.append('file', file);
xhr.open('POST', this.url);
xhr.send(data);
},
/**
* Respond to XHR2 'onload' event.
*/
onLoad: function (xhr) {
var response = $.parseJSON(xhr.responseText);
console.log(response); // Doesn't work!
}
});
Why is that so, and how can I move the code outside of send() and into a separate function?
You're calling the function with this.onLoad(xhr) rather than passing a function reference. Try
var self = this;
xhr.onload = function () {
self.onLoad(xhr);
};
So, thanks to Musa and Jonathan Lonowski I now have following, working code:
var Photos = Backbone.Collection.extend({
url: config.url,
/**
* Send file to server.
* #todo Should Backbone.sync be overwritten instead?
*/
send: function (file) {
var data = new FormData(),
xhr = new XMLHttpRequest();
xhr.addEventListener('load', this.onLoad);
data.append('file', file);
xhr.open('POST', this.url);
xhr.send(data);
},
/**
* Respond to XHR2 'onload' event.
*
* No need to pass in the xhr object, since addEventListener
* automatically sets 'this' to 'xhr'.
*/
onLoad: function () {
var response = $.parseJSON(xhr.responseText);
console.log(response); // Works now!
}
});

Categories

Resources