I'm trying to implement a upload progress bar the HTML5 way, by using the XMLHttpRequest level 2 support for progress events.
In every example you see, the method is to add an event listener to the progress event like so:
req.addEventListener("progress", function(event) {
if (event.lengthComputable) {
var percentComplete = Math.round(event.loaded * 100 / event.total);
console.log(percentComplete);
}
}, false);
Such examples always seem to assume that event.lengthComputable will be true. After all, the browser knows the length of the request it's sending, surely?
No matter what I do, event.lengthComputable is false. I've tested this in Safari 5.1.7 and Firefox 12, both on OSX.
My site is built using Django, and I get the same problem on my dev and production setups.
The full code I'm using to generate the form upload is shown below (using jQuery):
form.submit(function() {
// Compile the data.
var data = form.serializeArray();
data.splice(0, 0, {
name: "file",
value: form.find("#id_file").get(0).files[0]
});
// Create the form data.
var fd = new FormData();
$.each(data, function(_, item) {
fd.append(item.name, item.value);
});
// Submit the data.
var req = new XMLHttpRequest();
req.addEventListener("progress", function(event) {
if (event.lengthComputable) {
var percentComplete = Math.round(event.loaded * 100 / event.total);
console.log(percentComplete);
}
}, false);
req.addEventListener("load", function(event) {
if (req.status == 200) {
var data = $.parseJSON(event.target.responseText);
if (data.success) {
console.log("It worked!")
} else {
console.log("It failed!")
}
} else {
console.log("It went really wrong!")
}
}, false);
req.addEventListener("error", function() {
console.log("It went really really wrong!")
}, false);
req.open("POST", "/my-bar/media/add/");
req.setRequestHeader("X-Requested-With", "XMLHttpRequest");
req.send(fd);
// Don't really submit!
return false;
});
I've been tearing my hair out for hours on this. Any help appreciated!
Hey I found the answer from #ComFreek:
I made the same mistake.
The line I wrote was:
xhr.onprogress = uploadProgress;
The correct one should be
xhr.upload.onprogress = uploadProgress;
take a look into this :
https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications
xhr.upload.addEventListener('progress',function(e){}) will also work.
I also had problem with sending multiple big files using AJAX (xmlhttprequest).
Found a solution and here is whole script that I use.
All you need is to place next line in your HTML page:
<input type="file" multiple name="file" id="upload_file" onchange="handleFiles(this)">
and use next script:
<script type="text/javacript">
var filesArray;
function sendFile(file)
{
var uri = "<URL TO PHP FILE>";
var xhr = new XMLHttpRequest();
var fd = new FormData();
var self = this;
xhr.upload.onprogress = updateProgress;
xhr.addEventListener("load", transferComplete, false);
xhr.addEventListener("error", transferFailed, false);
xhr.addEventListener("abort", transferCanceled, false);
xhr.open("POST", uri, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText); // handle response.
}
};
fd.append('myFile', file);
// Initiate a multipart/form-data upload
xhr.send(fd);
}
function updateProgress (oEvent)
{
if (oEvent.lengthComputable)
{
var percentComplete = oEvent.loaded / oEvent.total;
console.log(Math.round(percentComplete*100) + "%");
} else {
// Unable to compute progress information since the total size is unknown
console.log("Total size is unknown...");
}
}
function transferComplete(evt)
{
alert("The transfer is complete.");
}
function transferFailed(evt)
{
alert("An error occurred while transferring the file.");
}
function transferCanceled(evt)
{
alert("The transfer has been canceled by the user.");
}
function handleFiles(element)
{
filesArray = element.files;
if (filesArray.length > 0)
{
for (var i=0; i<filesArray.length; i++)
{
sendFile(filesArray[i]);
}
filesArray = '';
}
}
</script>
Your result will be in console
Related
So I have a webpage with two image elements. It is basically a website where you upload an image and it encrypts a secret massage with steganography. I want to show the difference that is not otherwise visible and I found Resemble.js which is a library to compare images. It gets two files as arguments and I would like to use my image sources instead of files since I don't want to save the images generated.
To sum up, I want to get rid of the requests and get my images via sources in the HTML but I don't know how to get it to work with Resemble.js since it accepts only files.
How the second image is generated:
cover.src = steg.encode(textarea.value, img, {
"width": img.width,
"height": img.height
});
The JavaScript working with files:
(function () {
var xhr = new XMLHttpRequest();
var xhr2 = new XMLHttpRequest();
var done = $.Deferred();
var dtwo = $.Deferred();
try {
xhr.open('GET', 'static/original.png', true);
xhr.responseType = 'blob';
xhr.onload = function (e) { done.resolve(this.response); };
xhr.send();
xhr2.open('GET', 'static/encoded.png', true);
xhr2.responseType = 'blob';
xhr2.onload = function (e) { dtwo.resolve(this.response); };
xhr2.send();
} catch (err) {
alert(err);
}
$('#example-images').click(function () {
$.when(done, dtwo).done(function (file, file1) {
if (typeof FileReader === 'undefined') {
resembleControl = resemble('./static/original.png')
.compareTo('./static/encoded.png')
.onComplete(onComplete);
} else {
resembleControl = resemble(file)
.compareTo(file1)
.onComplete(onComplete);
}
});
return false;
});
}());
I have this code that I am using and it works fine in chrome, but in IE, it looks like that onreadystatechenge is not firing.
How do I get this to work cross browser. I read that in IE you have to place the onreadystatechange event before the send, But that didn't work.
The alert here is not firing. And yes it is successful.
if (xhr.status==200 && xhr.readyState==4)
{
alert("DONE!");
}
This is the entire request.
function SendFile(evt)
{
var xhr = new XMLHttpRequest();
var data = new FormData();
var files = $("#FileUpload1").get(0).files;
for (var i = 0; i < files.length; i++)
{
data.append(files[i].name, files[i]);
}
xhr.upload.addEventListener("progress", function (evt)
{
if (evt.lengthComputable)
{
var progress = Math.round(evt.loaded * 100 / evt.total);
$("#progressbar").progressbar("value", progress);
}
}, false);
xhr.open("POST", "Handler.ashx");
xhr.onreadystatechange=function ()
{
if (xhr.status==200 && xhr.readyState==4)
{
alert("DONE!");
}
}
xhr.send(data);
$("#progressbar").progressbar({
max: 100,
change: function (evt, ui)
{
$("#progresslabel").text($("#progressbar").progressbar("value") + "%");
},
complete: function (evt, ui)
{
$("#progresslabel").text("File upload successful!");
GetID();
}
});
}
Tried onload with no luck.
xhr.onload = function ()
{
if (xhr.readyState == 4 && xhr.status==200)
{
alert("IE DONE!");
}
}
Your XMLHttpRequest object might be caching.
Try:
var myNoCachePage = document.location + '?' + new Date().getTime();
xhr.open("GET", myNoCachePage, true); // Prevent IE11 from caching
xhr.send();
Apparently IE 11 (which I assume is what you are testing is broken) removed script.onreadystatechange in favor of script.onload.
Here is the compatibility for xhr.onload.
Source of IE11 removing it
I am sure there is something that I am missing from this code, I just can not figure out what it is.
Here is the main page:
<script>
function wwCallback(e) {
document.write(e.data);
}
function wwError(e) {
alert(e.data)
}
$(document).ready(function () {
var worker = new Worker("sync.js");
worker.onmessage = wwCallback;
worker.onerror = wwError;
worker.postMessage({
'cmd': 'downloadUser',
'url': 'server.php'
});
console.log("WW Started");
});
</script>
Server.php simply echoes a JSON string and I have verified that it is both valid and working using normal ajax requests.
Here is my web workers code:
function getData(url) {
var req = new XMLHttpRequest();
//expect json
req.open('GET', url);
req.send(null);
req.onreadystatechange = function () {
if (req.readyState == 4) {
if (req.status == 200) {
self.postMessage(req.responseText);
}
}
}
}
self.addEventListener('message', function (e) {
var data = e.data;
switch (data.cmd) {
case 'downloadUser':
getData(data.url);
}
self.close();
}, false);
Could anyone point me in the right direction?
Your problem is in your WebWorker XHR call. You've opened the request, then immediately sent it before you've set up the event handler to handle the response. You just need to move the req.send(null); call below the code that sets up the event handler.
Try this:
function getData(url) {
var req = new XMLHttpRequest();
//expect json
req.open('GET', url);
// req.send(null); // remove this line from here.
req.onreadystatechange = function () {
if (req.readyState == 4) {
if (req.status == 200) {
self.postMessage(req.responseText);
}
}
}
req.send(null); // make the request after your readystatechange
// handler is set up.
}
self.addEventListener('message', function (e) {
var data = e.data;
switch (data.cmd) {
case 'downloadUser':
getData(data.url);
}
self.close();
}, false);
So I am using a ajax to upload multiple files. Everything seems to be working like a charm... I just can't get to make my progress bars to work ...
Any help would be really appreciated. Thanks.
var images = document.getElementById('images');
for(var i=0;i<images.files.length;i++) {
var formData = new FormData();
var image = images.files[i];
formData.append('image', image);
formData.append('order_id', document.getElementById('order_id').value);
var xmlhttp=new XMLHttpRequest();
xmlhttp.open("POST","/pictures/uploadImage");
xmlhttp.send(formData);
xmlhttp.upload.addEventListener('progress', function(e){
document.getElementById("image_"+i+"_progress").value = Math.ceil(e.loaded/e.total)*100;
}, false);
}
I am basically uploading images individually .. I figured that would help me track the progress bars better ... Perhaps there's another approach.
According to [MDN][1]:
Note: You need to add the event listeners before calling open() on the request. Otherwise the progress events will not fire.
So, combining this knowledge with Engin's answer, you could do like this:
const images = document.getElementById('images');
const completedCount = 0; // added for loadend scenario
const length = images.files.length;
for (let i = 0; i < length; i++) {
const formData = new FormData();
const image = images.files[i];
formData.append('image', image);
formData.append('order_id', document.getElementById('order_id').value);
const xmlhttp = new XMLHttpRequest();
(elId => {
xmlhttp.upload.addEventListener('progress', e => {
document.getElementById('image_' + elId + '_progress').value = Math.ceil(e.loaded / e.total) * 100;
}, false);
})(i); // to unbind i.
// --- added for loadend scenario. ---
xmlhttp.addEventListener('loadend', () => {
completedCount++;
if (completedCount == length) {
// here you should hide your gif animation
}
}, false);
// ---
xmlhttp.open('POST', '/pictures/uploadImage');
xmlhttp.send(formData);
}
UPDATE:
To catch the event when all files are uploaded you may use loadend events. I've updated my code (see comments), I'm not sure this is a correct way though.
[1]: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Monitoring_progress
i did not try but i think it works, because for loop finished before your post and "i"s value equal to images.files.length. sorry for terrible english
try this:
var images = document.getElementById('images');
for(var i=0;i<images.files.length;i++) {
getProgress(images.files[i],i);
}
function getProgress(image,order){
var formData = new FormData();
formData.append('image', image);
formData.append('order_id', document.getElementById('order_id').value);
var xmlhttp=new XMLHttpRequest();
xmlhttp.open("POST","/pictures/uploadImage");
xmlhttp.send(formData);
xmlhttp.upload.addEventListener('progress', function(e){
document.getElementById("image_"+order+"_progress").value = Math.ceil(e.loaded/e.total)*100;
}, false);
}
You can include another function.
function upload(ProgressbarId){
/*
.
.
.
some code
*/
xmlhttp.upload.addEventListener("progress", function(evt){uploadProgress(evt,ProgressbarId);}, false);
}
function uploadProgress(evt,ProgressbarId) {
if (evt.lengthComputable) {
var percentComplete = Math.round(evt.loaded * 100 / evt.total);
set_Progressbar(ProgressbarId,percentComplete);
}
}
}
I'm using FormData to ajax a file upload. The upload works, but the problem is that the "error" callback is never invoked. Even when my HTTP response is a 500 internal server error (to test this I tweak server to respond with 500), the "load" callback is invoked.
function upload_image() {
var form = document.getElementById('upload_image_form');
var formData = new FormData(form);
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", function(e) {
alert("Success callback");
}, false);
xhr.addEventListener("error", function(e) {
alert("Error callback");
}, false);
xhr.open("POST", "/upload_image");
xhr.send(formData);
}
Any ideas? I'm testing this on Chrome.
This setup should work better for your needs:
var req = new XMLHttpRequest();
req.open('POST', '/upload_image');
req.onreadystatechange = function (aEvt) {
if (req.readyState == 4) {
if(req.status == 200)
alert(req.responseText);
else
alert("Error loading page\n");
}
};
req.send(formData);
In your code error callback is never called because it is only triggered by network-level errors, it ignores HTTP return codes.
The load event is called whenever the server responds with a message. The semantics of the response don't matter; what's important is that the server responded (in this case with a 500 status). If you wish to apply error semantics to the event, you have to process the status yourself.
Expanding on #rich remer's answer, here's how you could access the status yourself:
function upload_image() {
var form = document.getElementById('upload_image_form');
var formData = new FormData(form);
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", function(e) {
if(e.currentTarget.status < 400)
alert("Load callback - success!");
else
alert("Load callback - error!");
}, false);
xhr.addEventListener("error", function(e) {
alert("Error callback");
}, false);
xhr.open("POST", "/upload_image");
xhr.send(formData);
}
Please note accessing of the e.currentTarget.status property of the response event (e). Looks like the status is actually available via any of e.{currentTarget,target,srcElement}.status - I'm not sure which one should be used as the best practice, though.
function get(url) {
return new Promise(function(succeed, fail) {
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.addEventListener("load", function() {
if (req.status < 400)
succeed(req.responseText);
else
fail(new Error("Request failed: " + req.statusText));
});
req.addEventListener("error", function() {
fail(new Error("Network error"));
});
req.send(null);
});
}
code from EJS
by the example code
it is clear that network error has no response, it trigger error event.
response trigger load event
and you have to decide what to do with the response status