XMLHttpRequest event.loaded behaviour changed? - javascript

Some time ago, I wrote a script that looks something like this:
$("#file-upload-button").click(function(e) {
e.preventDefault();
var data = new FormData();
jQuery.each($('#file-upload')[0].files, function(i, file) {
data.append('file', file);
});
$.ajax({
type: "POST",
url: self.urlroot + '/file/upload',
data: data,
contentType: false,
processData: false,
xhr: function() {
var xhr = new window.XMLHttpRequest();
//Upload progress
xhr.upload.addEventListener("progress", function(evt){
if (evt.lengthComputable) {
var percentComplete = evt.loaded / evt.total;
console.log(percentComplete);
//more stuff
}
}, false);
//Download progress
xhr.addEventListener("progress", function(evt){
//something similar to above
}, false);
return xhr;
},
success: function( response ) {
//stuff
},
error: function() {
//stuff
}
});
});
And it worked just fine. Today I tried to reuse the code on another page, and I've determined that evt.loaded is no longer tracking how much has been sent to the server, but is instead tracking how much of the file has been loaded into memory to be sent to the server. So it goes to 100% almost immediately, because the file loads to memory very quickly, while not giving an accurate indication of how much has been sent to the server at all. I tried updating my code to something like the following, to no avail:
$("#file-upload-button").change(function(e) {
e.preventDefault();
$("button").attr("disabled", true).fadeTo("fast", 0.5);
var data = new FormData();
jQuery.each($('#file-upload')[0].files, function(i, file) {
data.append('file', file);
});
var xhr = new XMLHttpRequest;
xhr.upload.onprogress = function (evt) {
var percentComplete = evt.loaded / evt.total;
console.log(percentComplete, evt.loaded, evt.total);
//some stuff
};
xhr.upload.onload = function (e) {
//some stuff
};
xhr.upload.onerror = function (e) {
//some stuff
};
xhr.open("POST", "Owl/");
xhr.send(data);
});
In both instances, it shows evt.loaded to be the same as evt.total almost immediately, and then several seconds later based on the size of the file, the upload actually finishes and the response is processed. Any idea how I can fix this to actually show progress on the upload?

Related

Progress bar for multiple ajax requests with stages of completion. Is it possible?

I have a simple form which when submitted validates via an ajax request. If the form checks out ok, then another ajax request is made to process the data originally submitted.
I want to build a progress bar for this. Ive found that adding this code to each ajax request returns the progress for each call separately. That makes the progress bar load to 100%, twice, quickly.
Is it possible for example for two ajax request to each fill 50% of the progress bar?... So ajax request 1 will fill up to 50% and the second will fill from 51% to 100%? Or is that crazy?
Or if three ajax calls each being responsible for 33.33% of the total percentage?
I guess we are more looking at stages of completion as well as progress.
Any ideas how this could be achieved without too much faking it?
var xhr = new window.XMLHttpRequest();
//Upload progress
xhr.upload.addEventListener("progress", function (evt) {
if (evt.lengthComputable) {
var percentComplete = evt.loaded / evt.total;
//Do something with upload progress
console.log('percent uploaded: ' + (percentComplete * 100));
}
}, false);
//Download progress
xhr.addEventListener("progress", function (evt) {
if (evt.lengthComputable) {
var percentComplete = evt.loaded / evt.total;
//Do something with download progress
console.log('percent downloaded: ' + (percentComplete * 100));
}
}, false);
return xhr;
Well the way i had created such a progress bar was, since you want each of your function to be called one after another, that is completion of one should trigger the other, XMLHttpRequest has onreadystate change event. you can use that event to confirm that the first request got executed successfully or not, and then trigger the second one, at each change you will have progress bar incremented by whatever % you want to.
function postdata()
{
var xhr = new XMLHttpRequest();
xhr.open
(
"POST",
Url,
true
);
xhr.send();
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
//Call next function here and increment progressbar
}
}
}
hope this helps
Yes, it is possible. You can create an array containing each ajax call. Set <progress> element max attribute to 100/array.length. Divide evt.loaded / evt.total of individual progress event by array .length to set value of <progress> element. You could also use Promise.all(), .then() to process array of functions returning a Promise from ajax call and update <progress> element.
html
<label></label>
<progress value="0" min="0" max="100"></progress>
javascript
var progress = document.querySelector("progress");
var url = "/echo/html/";
function request(url) {
var len = arr.length;
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", function(evt) {
if (evt.lengthComputable) {
var percentComplete = ((evt.loaded / evt.total) * (progress.max / len))
/ len;
progress.value += percentComplete;
console.log(progress.value, percentComplete);
if (evt.total === evt.loaded) {
requests += 1;
}
if (progress.value == progress.max && requests === len) {
progress.previousElementSibling.innerHTML = "upload complete";
// you could call `resolve()` here if only interested in
// `upload` portion of request
alert("upload complete");
}
}
}, false);
xhr.onload = function() {
resolve(this.responseText)
};
xhr.onerror = reject;
xhr.open("POST", url, true)
xhr.send("html=" + Array(100000).fill(1).join(""));
})
}
var arr = [], requests = 0;
arr.push(request, request, request);
Promise.all(arr.map(function(req) {
return req(url)
}))
.then(function(data) {
console.log(data);
})
.catch(function(err) {
console.log(err)
})
jsfiddle https://jsfiddle.net/v2msL7hj/3/

XMLHttpRequest: need to use success and error functions from ajax

I have this ajax post function run when an upload button is clicked, the files get uploaded to the server, the server sees for any errors and if there are erros, it notifies the user with the req.end(); function. The problem is, since then, I've moved to XMLHttpRequest() (to use the onprogress functions that it provides) but I still need to use those success and error functions from ajax. Is there a way to use them somehow with XMLHttpRequest? Thank you!
This is the code I have so far:
var xhrVideo = new XMLHttpRequest();
xhrVideo.open('POST', '/uploadVideo', true);
xhrVideo.upload.onprogress = function(e) {
if (e.lengthComputable) {
$('.videoProgress').html(((e.loaded / e.total) * 100).toFixed(0)+'%');
}
};
var videoForm = new FormData($('.videoUploadForm')[0]);
xhrVideo.send(videoForm);
And the ajax code:
var videoData = new FormData($('.videoUploadForm')[0]);
$.ajax({
url: '/uploadVideo',
type: 'POST',
data: videoData,
cache: false,
contentType: false,
processData: false,
mimeType:"multipart/form-data",
success: function(data){
switch(data){
case '1':
alert('Wrong Video File Extension');
break;
case '2':
alert('Wrong Image File Type');
break;
}
},
error: function(data){
alert('Something went wrong! Please reload this page')
}
});
Use listeners for load and error events.
Example adding a listener for success event.
xhrVideo.addEventListener('load', function(e) {
NOTE: the listeners must be added before the send() function
Also, I advise you to read the entire article about using XMLHttpRequest().
Personally , I like to use jquery ajax instead of pure javascript .. so you can use xhr with ajax and catch the progress and load event as well
xhr (default: ActiveXObject when available (IE), the XMLHttpRequest
otherwise) Type: Function() Callback for creating the XMLHttpRequest
object. Defaults to the ActiveXObject when available (IE), the
XMLHttpRequest otherwise. Override to provide your own implementation
for XMLHttpRequest or enhancements to the factory.
in your code you can use it like this
$.ajax({
xhr: function(){
var xhr = new window.XMLHttpRequest();
//Upload progress
xhr.upload.addEventListener("progress", function(evt){
if (evt.lengthComputable) {
var percentComplete = (evt.loaded / evt.total) * 100;
//Do something with upload progress
$('.videoProgress').html(Math.round(percentComplete)+"% uploaded");
}
}
}, false);
xhr.upload.addEventListener("load", function(evt){
evt.preventDefault();
},false);
return xhr;
},
// reset of your ajax code

Feed FileReader from server side files

I´m starting to customize/improve an old audio editor project. I can import audio tracks to my canvas VIA drag&drop from my computer. The thing is that I also would like to use audio tracks already stored in the server just clicking over a list of available tracks... instead of use the <input type="file"> tags. How can I read the server side files with a FileReader?Ajax perhaps? Thanks in advance.
This is the code for the file reader:
Player.prototype.loadFile = function(file, el) {
//console.log(file);
var reader = new FileReader,
fileTypes = ['audio/mpeg', 'audio/mp3', 'audio/wave', 'audio/wav'],
that = this;
if (fileTypes.indexOf(file.type) < 0) {
throw('Unsupported file format!');
}
reader.onloadend = function(e) {
if (e.target.readyState == FileReader.DONE) { // DONE == 2
$('.progress').children().width('100%');
var onsuccess = function(audioBuffer) {
$(el).trigger('Audiee:fileLoaded', [audioBuffer, file]);
},
onerror = function() {
// on error - show alert modal
var tpl = (_.template(AlertT))({
message: 'Error while loading the file ' + file.name + '.'
}),
$tpl = $(tpl);
$tpl.on('hide', function() { $tpl.remove() })
.modal(); // show the modal window
// hide the new track modal
$('#newTrackModal').modal('hide');
};
that.context.decodeAudioData(e.target.result, onsuccess, onerror);
}
};
// NOTE: Maybe move to different module...
reader.onprogress = function(e) {
if (e.lengthComputable) {
$progress = $('.progress', '#newTrackModal');
if ($progress.hasClass('hide'))
$progress.fadeIn('fast');
// show loading progress
var loaded = Math.floor(e.loaded / e.total * 100);
$progress.children().width(loaded + '%');
}
};
reader.readAsArrayBuffer(file);
};
return Player;
Thanks for the suggestion micronn, I managed to make a bypass without touch the original code. The code as follows is the following:
jQuery('.file_in_server').click(function()
{
var url=jQuery(this).attr('src');//Get the server path with the mp3/wav file
var filename = url.replace(/^.*[\\\/]/, '');
var path="http://localhost/test/audio/tracks/"+filename;
var file = new File([""], filename); //I need this hack because the original function recives a buffer as well as the file sent from the web form, so I need it to send at least the filename
var get_track = new XMLHttpRequest();
get_track.open('GET',path,true);
get_track.responseType="arraybuffer";
get_track.onload = function(e)
{
if (this.status == 200) //When OK
{
Audiee.Player.context.decodeAudioData(this.response,function(buffer){ //Process the audio toward a buffer
jQuery('#menu-view ul.nav').trigger('Audiee:fileLoaded', [buffer, file]); //Send the buffer & file hack to the loading function
},function(){
alert("Error opening file");
jQuery('#newTrackModal').modal('hide');
});
}
};
get_track.send();
});
After this, in the fileLoaded function, the track is added to the editor.
var name = 'Pista ' + Audiee.Collections.Tracks.getIndexCount();
track = new TrackM({buffer: audioBuffer, file: file, name: name}); //being audioBuffer my buffer, file the fake file and name the fake file name
Audiee.Collections.Tracks.add(track);
And... thats it!

XHR progress event not firing until upload completes?

I'm using the following $.ajax command to upload a file from a PhoneGap application:
function updateProgress( evt ) {
if ( evt.lengthComputable ) {
var percentComplete = evt.loaded / evt.total * 100;
console.log( percentComplete + "%" );
}
}
$.ajax({
url: url,
type: "POST",
data: data,
cache: false,
dataType: "json",
processData: false,
contentType: false,
success: successCallback,
error: errorCallback,
xhr: function() {
var xhr = new window.XMLHttpRequest();
xhr.addEventListener( "progress", updateProgress, false);
return xhr;
}
});
The upload works fine. However the progress event only fires one time, once the upload has completed. It does not actually fire during uploading - so upload progress does not actually display. There is just a pause while it is uploading, and then it displays 100%.
Any ideas what I am doing wrong?
The upload progress events are fired on xhr.upload, so attach the listener to that rather than xhr. There are also progress events on the xhr object but this is for the response coming back from the server.
See the MDN article for more details.
xhr.upload.addEventListener('progress', updateProgress, false)
(Thanks to A. Wolff and his comment on the OP.)

XMLHttpRequest in google-chrome is not reporting progress events

Hi all I have this code:
function test()
{
req = new XMLHttpRequest();
req.upload.addEventListener("progress", updateProgress, false);
req.addEventListener("readystatechange", updateProgress, false);
req.addEventListener("error", uploadFailed, false);
req.addEventListener("abort", uploadCanceled, false);
var data = generateRandomData(currentPayloadId);
totalSize = data.length;
req.open("POST", "www.mydomain.com/upload.aspx");
start = (new Date()).getTime();
req.send(data);
}
function updateProgress(evt)
{
if (evt.lengthComputable) {
total = totalSize = evt.total;
loaded = evt.loaded;
}
else {
total = loaded = totalSize;
}
}
Also, my server responds to the initial OPTIONS request for upload.aspx with 200 and the Access-Control-Allow-Origin: *
and then the second request POST happens
Everything seems in place and it's working great on FireFox but on G Chrome the updateProgress handler is not getting called but only once and then the lengthComputable is false.
I needed the Access-Control-Allow-Origin: * because this is a cross-domain call, the script parent is a resource on a different server then the upload.aspx domain
Anyone can give me some clues, hints, help please? is this a known issue with G Chrome?
Thank you!
Ova
I think I have a solution for your problem
I don't know what is behind this function "generateRandomData()"
var data = generateRandomData(currentPayloadId)
It is working when I change into this:
var data = new FormData();
data.append("fileToUpload", document.getElementById('fileToUpload').files[0]);
Small explanation: You need manually to append to form data an file input form, where fileToUpload is <input type="file" name="fileToUpload" id="fileToUpload" />
And in your updateProgress function in IF part you can add something like this to track progress console.log(evt.total +" - "+ evt.loaded)
This is working in Google Chrome browser. I have tested in new browser version 57
I made for myself an upload progress form 4 years ago, which means that this code is working in old browser version too.
A whole code snippet will be looking like this
function test()
{
req = new XMLHttpRequest();
req.upload.addEventListener("progress", updateProgress, false);
req.addEventListener("readystatechange", updateProgress, false);
req.addEventListener("error", uploadFailed, false);
req.addEventListener("abort", uploadCanceled, false);
//var data = generateRandomData(currentPayloadId);
var data = new FormData();
data.append("fileToUpload", document.getElementById('fileToUpload').files[0]);
totalSize = data.length;
req.open("POST", "www.mydomain.com/upload.aspx");
start = (new Date()).getTime();
req.send(data);
}
function updateProgress(evt)
{
if (evt.lengthComputable) {
total = totalSize = evt.total;
loaded = evt.loaded;
console.log(evt.total +" - "+ evt.loaded)
}
else {
total = loaded = totalSize;
}
}
I had this problem when the page your are loading doesn't contain a
Content-Length: 12345
in the header, where 12345 is the length of the response in bytes. Without a length parameter, the progress function has nothing to work on.
First, make sure that "www.example.com" is added to the manifest.json, like so:
manifest.json
{
..
"permissions": [
"http://www.example.com/",
"https://www.example.com/",
],
..
}
Then I think your example should work.
For more information about using xhr in google chrome extensions the docs are here.
Also the CSP docs are worth taking a look at if what I provided above does not.
This could simply be a compatibility issue with the XMLHttpRequest.upload property. It returns an XMLHttpRequestUpload object, but if you try find that object spec in MDN it doesn't exist so how do we know which browsers fully support it.
XMLHttpRequest.upload Compatability
Have you tried listening for progress directly on the xhr:
req.addEventListener("progress", updateProgress, false);
I use jQuery for progress like that:
$.ajax({
url : furl,
type : method,
data : data,
//...
},
xhr : function () {
//upload Progress
var xhr = $.ajaxSettings.xhr();
if (xhr.upload) {
xhr.upload.addEventListener('progress', function (event) {
var percent = 0;
var position = event.loaded || event.position;
var total = event.total;
if (event.lengthComputable) {
percent = Math.ceil(position / total * 100);
}
//update progressbar
$(".progress-bar").css("width", + percent + "%");
$(" .status").text(position + " / " + total + " (" + percent + "%)");
}, true);
}
return xhr;
},

Categories