Multiple onprogress event handlers from xhr in loop - javascript

I'm currently trying to build an ajax image uploader and I have it working except for one important part.
For each separate file I upload I would like to update a progress bar for the file. Currently it only updates the last files progress bar.
$("#upload-btn").click(function() {
console.log("Click");
for (var i = 0; i < files.length; i++) {
var fd = new FormData();
fd.append("file", files[i]);
fd.append("__RequestVerificationToken", $("input[name='__RequestVerificationToken']").val());
var xhr = new XMLHttpRequest();
xhr.open('POST', "#Url.Action("AjaxUpload")", true);
var filename = files[i].name;
xhr.upload.onprogress = function (e) {
if (e.lengthComputable) {
var percentComplete = (e.loaded / e.total) * 100;
console.log(percentComplete + '% uploaded');
var progressbar = $('a[data-name="' + files[i].name + '"]').siblings(".progress").find(".progress-bar");
progressbar.css("width", percentComplete + "%");
}
};
xhr.onload = function () {
console.log(this.status);
if (this.status == 200) {
var resp = JSON.parse(this.response);
console.log('Server got:', resp);
};
};
xhr.send(fd);
}
});
As you may have guessed from the code, I'm pretty terrible at javascript... Any help is greatly appreciated.

You need to scope your file name in the progress callback, currently it will only use the last value of i. Replace where you set your onprogress handler with this code.
(function(filename) {
xhr.upload.onprogress = function (e) {
if (e.lengthComputable) {
var percentComplete = (e.loaded / e.total) * 100;
console.log(percentComplete + '% uploaded');
var progressbar = $('a[data-name="' + filename + '"]').siblings(".progress").find(".progress-bar");
progressbar.css("width", percentComplete + "%");
}
};
})(files[i].name);

You can do something like this. Set the additional parameters of xhr.upload
// Set custom properties
xhr.upload.targetFileName = fileName;
and then access it in progress handler like this
xhr.upload.onprogress = function (e) {
// Access custom properties.
var fileName = e.target.targetFileName;
}
More details here: https://hacks.mozilla.org/2009/06/xhr-progress-and-richer-file-uploading-feedback/ view-source:http://mozilla.pettay.fi/xhr_upload/xhr_file_upload_demo_with_speed.html

Related

ajax listener for file uploading progress

I cant find a way to get around my code. I have seen some tutorials out there, but because of the way I design my code I guess I need something different.
(function(){
var dropzone = document.getElementById('dropzone');
var uploadFile = function (files) {
var formData = new FormData();
var request = new XMLHttpRequest();
var i;
var file_size = 0;
for(i = 0; i<files.length; i = i+1){
formData.append('file[]', files[i]);
file_size = file_size + files[i].size;
}
request.onload = function(){
var data = this.responseText;
// window.location.reload();
};
request.open('post', 'dragNdrop.php?isNotSet=set');
request.send(formData);
};
dropzone.ondragover = function(){
this.className = 'dropzone dragover';
return false;
};
dropzone.ondragleave = function(){
this.className = 'dropzone';
return false;
};
dropzone.ondrop = function (event) {
event.preventDefault();
this.className = 'dropzone';
uploadFile(event.dataTransfer.files);
return false;
}
}());
This is the code I use to upload a file, but i cant find a way to add a eventlistener that keeps checking if the file has been uploaded. I have tried.
request.upload = function(){console.log("me")} //Returns nothing
I tried something like this:
request.upload.addEventListener("progress", progressBar, false);
function progressBar(event){
var percent = (event.loaded / event.total) * 100;
console.log(percent + " " + event.loaded + " " + event.total);
}
It only returns the value once, I would imagine it needs to return several times as it needs to loop in order to get the progress bar moving?
All this function below was inside the main function.
How else could I do this?

Sending two files in a queue

Hey I have a problem with sending two files in a queque. I don't have any idea how to do that when I select 2 files, 2 were uploaded at once, and the next 2 were waiting in queue. Now when I select 4 files, 2 are sent and the next two will not send. Please look in my code. Maybe you have an idea. What I must to do that make it work in function induction ?
(function() {
var imageType = /image.*/;
function uploadFile(file, percent_info, p_bar, licznik) {
var url = "server/index.php";
if (file[licznik].type.match(imageType)) {
var xhr = new XMLHttpRequest();
var fd = new FormData();
xhr.upload.addEventListener("progress", function(evt) {
if (evt.lengthComputable) {
var percentLoaded = Math.round((evt.loaded / evt.total) * 100);
if (percentLoaded < 100) {
percent_info[licznik].style.width = percentLoaded + "%";
}
}
});
xhr.upload.addEventListener("load", function(e) {
var percentLoaded = Math.round((e.loaded / e.total) * 100);
percent_info[licznik].style.width = percentLoaded + "%";
});
function ready() {
return function() {
if (xhr.readyState == 4 && xhr.status == 200) {
p_bar[licznik].classList.add('trans_completed');
if (licznik < file.length){
licznik++;
}
}
};
}
xhr.onreadystatechange = ready();
xhr.open("POST", url, true);
fd.append('uploaded_file', file[licznik]);
xhr.send(fd);
}
};
var uploadfiles = document.querySelector('#file-upload');
uploadfiles.addEventListener('change', function() {
var files = this.files;
var percent_info = document.querySelectorAll('.progress_bar:not(.trans_completed) .percent');
var p_bar = document.querySelectorAll('.progress_bar:not(.trans_completed)');
/* --- Upload files to server loop --- */
(function induction(files, percent_info, p_bar) {
counter_file = 0;
counter_file_2 = 1;
function caller() {
uploadFile(files, percent_info, p_bar, counter_file);
uploadFile(files, percent_info, p_bar, counter_file_2);
}
caller();
})(files, percent_info, p_bar);
});
})();

Find resultant of two asynchronous ajax request in xhr onprogress

I am trying to find images in ajax request response text and fetch the src url for using in another ajax request.
I need to know progress of loading each image and show the resultant of this progress in my progress bar.
But as you can see if image1 loaded 10%;
and image2 20%.
My result decreases from 20% to 10% .
My progress bar goes backward and then goes forward.
Any help?
or is this a good solution?
parseResult: function(result, navNumber) {
var self = this;
var imagesMatter = $(result).find('img');
var imagesLength = imagesMatter.length;
// if there is any image in ajax request then fetch imgs using ajax for show progress bar
if (imagesLength >= 1) {
var i = 0,
complete = 0,
temp = 1;
navigation.progress.fadeIn(50);
imagesMatter.each(function() {
var $this = $(this);
var URL = $this.attr('src');
$.ajax({
url: URL,
xhr: function() {
var xhr = new window.XMLHttpRequest();
xhr.addEventListener("progress", function(evt) {
if (evt.lengthComputable) {
var percentComplete = parseFloat(evt.loaded / evt.total).toFixed(1);
temp = complete + (percentComplete / imagesLength);
console.log(parseInt(temp * 100) + '%');
navigation.progress.html(parseInt(temp * 100) + '%');
}
}, false);
return xhr;
},
complete: function() {
complete = temp;
i++;
if (i == imagesLength) {
self.closeLoading(navNumber);
}
}
});
});
} else {
return;
}
}
Here I have removed complete variable of yours, as I can' understand what is the purpose of it, and added a local variable lastPercent for each request, which will remember it's last progress percent for the perticular image request, so on new update we will add new percent and remove old percents... I hope this logic is clear to you...
parseResult: function(result, navNumber) {
var self = this;
var showPercent;
var imagesMatter = $(result).find('img');
var imagesLength = imagesMatter.length;
// if there is any image in ajax request then fetch imgs using ajax for show progress bar
if (imagesLength >= 1) {
var i = 0,
temp = 1;
navigation.progress.fadeIn(50);
imagesMatter.each(function() {
var $this = $(this);
var URL = $this.attr('src');
var lastPercent = 0;
$.ajax({
url: URL,
xhr: function() {
var xhr = new window.XMLHttpRequest();
xhr.addEventListener("progress", function(evt) {
if (evt.lengthComputable) {
var percentComplete = parseFloat(evt.loaded / evt.total).toFixed(1);
temp = temp + (percentComplete / imagesLength) - lastPercent;
lastPercent = (percentComplete / imagesLength);
showPercent = temp/imagesLength;
console.log(parseInt(showPercent * 100) + '%');
navigation.progress.html(parseInt(showPercent * 100) + '%');
}
}, false);
return xhr;
},
complete: function() {
i++;
if (i == imagesLength) {
self.closeLoading(navNumber);
}
}
});
});
} else {
return;
}
}

Ajax loading bar function

I know there are a few post about this but they dont cover what i need to achieve.
I'm trying to get the youtubelike loading bar to work properly.
And i found this :
var data = [];
for (var i = 0; i < 100000; i++) {
var tmp = [];
for (var i = 0; i < 100000; i++) {
tmp[i] = 'hue';
}
data[i] = tmp;
};
xhr: function () {
var xhr = new window.XMLHttpRequest();
var percentComplete = 0; <--------------------------added this
$('.progress').removeClass('hide');<----------------and this
xhr.upload.addEventListener("progress", function (evt) {
if (evt.lengthComputable) {
var percentComplete = evt.loaded / evt.total;
console.log(percentComplete);
$('.progress').css({
width: percentComplete * 100 + '%'
});
if (percentComplete === 1) {
$('.progress').addClass('hide');
}
}
}, false);
xhr.addEventListener("progress", function (evt) {
if (evt.lengthComputable) {
var percentComplete = evt.loaded / evt.total;
console.log(percentComplete);
$('.progress').css({
width: percentComplete * 100 + '%'
});
}
}, false);
return xhr;
}
I have added 2 lines because it would only work once as the hidden value was not being removed after the completion. Also put the width back to 0. I also like the fact that it seams to be calculating the real percentage of the event.
This is working great. Howerver, i want to turn this into a function that can be called for all my ajax call like this:
$(document).ready(function () {
$("a").on("click", function (event) {
event.preventDefault();
var id = ($(this).attr("id"));
var container = ($(this).attr("data-container"));
var link = ($(this).attr("href"));
var text = ($(this).text());
var html = ($(this).html());
MY_LOADING_BAR_FUNCTION();<-----------------------------------Here i guess?
$.ajax({
url: 'ajax-php.php',
type: 'post',
data: { 'action': 'click', 'id': id, 'text': text, 'link': link, 'html': html },
success: function (data, status) {
if (container == '#formbox') {
$("#screen").addClass("visible");
}
$(container).html(data);
},
error: function (xhr, desc, err) {
console.log(xhr);
console.log("Details: " + desc + "\nError:" + err);
}
}); // end ajax call
}); // end on click
}); // en document ready
But i also ran across the Ajax setup that looks like this.
$.ajaxSetup({
beforeSend: function() {
},
complete : function() {
}
});
Right now i got it to work by putting the entire code of the xhr: function () inside my ajax call. But i dont want to do it every time.
So these are the 2 options i have found that could work but i cant get them to work the way i want. I try to make a MY_LOADING_BAR_FUNCTION() but i'm getting a deprecated error.
Can anyone tell me how to make this work please.
Thank you everyone!
It's late to reply, but its worth to share some knowledge, so others may get help from it.
You can extend jQuery AJAX XHR object as below.
jQuery.ajaxSettings.xhr = function ()
{
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;
}

Javascript - progress bar with muliple images

I want to preload a bunch of images and show the progress bar in the same time.
At the moment the code works only with 1 image, here is it:
$progress = document.querySelector('#progress');
var url = 'https://placekitten.com/g/2000/2000';
var request = new XMLHttpRequest();
request.onprogress = onProgress;
request.onload = onComplete;
request.onerror = onError;
function onProgress(event) {
if (!event.lengthComputable) {
return;
}
var loaded = event.loaded;
var total = event.total;
var progress = (loaded / total).toFixed(2);
$progress.textContent = 'Loading... ' + parseInt(progress * 100) + ' %';
console.log(progress);
}
function onComplete(event) {
var $img = document.createElement('img');
$img.setAttribute('src', url);
$progress.appendChild($img);
console.log('complete', url);
}
function onError(event) {
console.log('error');
}
$progress.addEventListener('click', function() {
request.open('GET', url, true);
request.overrideMimeType('text/plain; charset=x-user-defined');
request.send(null);
});
<div id="progress">Click me to load</div>
And here is the jsfiddle to test the code out: https://jsfiddle.net/q5d0osLr/
How can I make it work with more than one image?
Use an array to store each progress and create a function to load image and record the necessary information for you.
var $progress = document.querySelector('#progress');
var url = 'https://placekitten.com/g/';
var urls =
['https://i.imgur.com/7raUYR2.jpg', 'https://i.imgur.com/i8GSA1b.jpg', 'https://i.imgur.com/jGkaxEZ.jpg',
'http://i.imgur.com/cuS5DDv.jpg', 'https://i.imgur.com/bl5DOR0.jpg', 'http://i.imgur.com/TuFu2lK.jpg',
'https://i.imgur.com/qbwarWi.jpg', 'https://i.imgur.com/hJ9Ylph.gif', 'https://i.imgur.com/8KLbDxe.jpg',
'https://upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Pleiades_large.jpg/1024px-Pleiades_large.jpg',
];
// Store all progress.
var allProgress = [];
var loadImage = function(url) {
// Get current index.
var idx = allProgress.length;
// Push a progress in array.
allProgress.push(0.0);
var onProgress = function(evt) {
if (!evt.lengthComputable) {
return;
}
var loaded = evt.loaded;
var total = evt.total;
var progress = (loaded / total);
allProgress[idx] = progress;
// Calculate the progress by sum then divide.
var sum = allProgress.reduce(function(sum, val) {
return sum + val;
});
sum /= allProgress.length;
sum = sum.toFixed(2);
$progress.textContent = 'Loading... ' + parseInt(sum * 100) + ' %';
console.log(progress, idx);
};
var onComplete = function(evt) {
var $img = document.createElement('img');
$img.setAttribute('src', url);
document.body.appendChild($img);
console.log('complete', url);
};
var onError = function(evt) {
// You may have to do something with progress.
console.log('error');
};
// Move the request in function. So each of them is independent.
var request = new XMLHttpRequest();
request.onprogress = onProgress;
request.onload = onComplete;
request.onerror = onError;
request.open('GET', url, true);
request.overrideMimeType('text/plain; charset=x-user-defined');
request.send(null);
};
$progress.addEventListener('click', function() {
urls.forEach(function(url) {
loadImage(url);
});
});
img {
width: 100px;
height: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id="progress">Click me to load</div>

Categories

Resources