I am uploading files to server using Dojo 1.10. For the upload I use module dojo/request/xhr and trying to display progress in percents. I am confused that the progress callback is fired just one and only at the end of the transfer. The file is transferred successfully. See fragment of my code:
function uploadFile(){
require([
'dojo/dom',
'dojo/request/xhr'
], function(dom, xhr) {
//... some unimportant code here
// Upload file now:
xhr(targetURL, {
handleAs: 'text',
method: 'POST',
headers: {'X-CSRF-Token': getAuthToken(), 'accept-charset': 'UTF-8'},
data: formData
}).then(function(data){
// Success => refresh file list
refreshDocList();
}, function(err){
// Failed
uploadFailed(err);
}, function(evt){
// Progress of upload
console.log(evt);
dom.byId('progress').innerHTML = 'Done ' + (evt.loaded * 100 / evt.total) + '%';
});
});
}
I tested it in FireFox (45.0.1, Windows 8.1), Chrome (49.0.2623.110 m, Windows 8.1), MSIE (11.0.9600.18231, Windows 8.1), FireFox (44.0, Ubuntu 15.04), Chrome (48.0.2564.116, Ubuntu 15.04). In none of the mentioned browsers the progress callback is called as expected. Any tip how to solve my problem?
It doesn't look like there's any way of doing this in dojo/request. You'll need to use XMLHttpRequest directly.
The reason is that progress events are only emitted for the download portion. For the upload portion, you need to use the upload member of the XHR object as follows:
var oReq = new XMLHttpRequest();
oReq.upload.addEventListener("progress", updateProgress);
oReq.upload.addEventListener("load", transferComplete);
oReq.upload.addEventListener("error", transferFailed);
oReq.upload.addEventListener("abort", transferCanceled);
oReq.open();
Looking at the dojo/request/xhr source code, I don't think there's any simple way of getting dojo/request/xhr to expose the XHR object (and by extension the upload member). Hence you'll probably need to use XMLHttpRequest directly.
For more, see https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
Further to #yassam's answer, it seems there is a way to access the xhr object that is behind the scenes of dojo/request, but for some reason I can only get this to work when I pass true as the third argument to request().. this is the returnDeferred argument, and if you check dojo/request/xhr code it has this final line:
return returnDeferred ? dfd : dfd.promise;
So, you can do this:
var promise = request('some_url', { .. options .. }, true); // <-- note 'true'!
if (promise.response.xhr) {
promise.response.xhr.upload.addEventListener("progress", function (e) {
console.log('xhr progress: ' + e.loaded + ' of ' + e.total + ': ', e);
});
}
One thing I don't understand right now, passing false for returnDeferred (or omitting it) should return dfd instead of dfd.promise from request(), in which case you should be able to use dfd.promise in exactly the same way, but I find this doesn't exist.
Related
I'm working on a web app which makes multiple API calls using jQuery's ajax. Because all of these requests are to my own API and use very similar sets of data, I'm hoping to use the features of either the $.ajaxSetup() or $.ajaxPrefilter() to make a generalized config, avoiding repetition and and reducing the footprint of my app significantly.
I'm using a function in the xhr config to calculate upload progress as a percentage which would also need to move. The behaviour of this is to be the same in every API call, but what I was hoping to do is provide a smaller "progress" function, unique to each ajax call that can then do whatever it wants with that percentage data once calculated. This would allow for easy operation and minimal code in all subsequent ajax calls.
If everything is written into the same ajax call as below - it works great! The progress function logs upload progress to console as expected.
$.ajax({
xhr: function() {
var xhr = new window.XMLHttpRequest();
xhr.upload.addEventListener("progress", function(evt) {
if (evt.lengthComputable) {
this.progress_percentage = parseInt(evt.loaded / evt.total * 100);
if(typeof this.progress === 'function'){
this.progress(this.progress_percentage); // <--- Run ajax-call specific function if defined.
}
}else{
console.warn('Content-length is not set. Unable to display upload progress.');
}
}, false);
return xhr;
},
progress: function(e) {
console.log('Called by xhr(). Progress: ' + e) // <-- Output logged successfully.
}
});
The difficulty comes in when moving the xhr function into the generalized config.
Because at that point the ajax object has not been created yet, I'm unsure of how to access it and check for the presence of the progress() function. To illustrate:
$.ajaxPrefilter(function( options, originalOptions, jqXHR ) {
options.xhr = function() {
var xhr = new window.XMLHttpRequest();
xhr.upload.addEventListener("progress", function(evt) {
if (evt.lengthComputable) {
this.progress_percentage = parseInt(evt.loaded / evt.total * 100);
if(typeof this.progress === 'function'){
this.progress(this.progress_percentage); // <--- progress function will NEVER be defined at this point because as far as prefilter is concerned, the ajax object has not been created yet and no call-specific behaviour can be set. "this" points to the window and NOT the ajax object.
}
}else{
console.warn('Content-length is not set. Unable to display upload progress.');
}
}, false);
return xhr;
}
});
I was expecting there to be some way to access the ajax object from it's setup functions but have been unable to find anything in the documentation. Hoping for either a way to do that, or some advice on how to refactor my code to get this running!
I realise upload progress is a fairly simple function and there will certainly be a lib for it, but as I have other configs and features within the app that would also benefit, I'd much prefer to get this running instead, if at all possible.
What would you suggest? Thank you.
I'm working on an uploader for angular-js that can give me progress updates. The code below works however on chrome, for files that are under around 3MB, one progress update fires before it starts sending, and the event data indicates that all the bytes are uploaded. It then takes some time (up to 30secs) before the file data is actually sent (and no further progress events will fire during the actual upload). If I use a larger file, I'll get two events or maybe three. On firefox, I'll get around 6 progress events for a 2MB file. Does anyone know if there's a way to adjust the granularity of these updates to fix this. To me, this function is basically useless (on chrome) since most documents aren't that big. Thanks.
send: function() {
var document = {
DocumentName: this.fsEntry.file.name
};
var formdata = new FormData();
formdata.append('file', this.fsEntry.file);
formdata.append('metadata', JSON.stringify(document));
this.xhr = new XMLHttpRequest();
this.xhr.addEventListener("loadstart", this.onLoadStart, false);
this.xhr.upload.addEventListener("progress", this.onProgress, false);
this.xhr.addEventListener("load", this.onComplete, false);
this.xhr.open("POST", this.fsEntry.uploadUri, true);
this.xhr.send(formdata);
}
this.onProgress = function(e) {
if (e.lengthComputable) {
console.log("progress: uploaded " + e.loaded + " of " + e.total);
}
}
I have been searching and have found some similar questions about this problem but thing still doesn't work for me at all.
Here is my function :
function ajaxRequest(url, type, datatype, contenttype, data, displayLoadingImg){
var result="";
displayLoadingImg = (typeof displayLoadingImg === "undefined") ? 0 : displayLoadingImg;
if(displayLoadingImg == 1){
$("#loadingImgAging").css("display","block");
}
$.ajax({
url:url,
type:type,
dataType:datatype,
contentType: contenttype,
data : JSON.stringify(data),
success:function(res){
result = res;
if(displayLoadingImg == 1){
$("#loadingImgAging").css("display","none");
}
},
error:function(res){
result="";
},
async : false
});
return result;
}
How I call it :
setTimeout(ajaxRequest(url,"GET","json","application/json",0,1), 500);
I've tried using beforeSend() but it did not work neither.
Note : If I delete async : false or put it to true, I'll get error Cross-Origin Request Blocked.... in my browser console.
My guess is you cannot do an ajax request with local files. There are ways to do this, but i prefer these two methods
Create a simple server
I usually use python for creating a simple server. Type this in your terminal/console: "python -m SimpleHTTPServer 9081" (or any port number you want to). Then, just open your localhost referring to your specified port.
Open chrome in --allow-file-access-from-files mode
or create a batch file using this following code in Windows. Chrome must be restarted before the configuration takes place.
start "chrome" "C:\Program Files (x86)\Google\Chrome\Application\chrome" --allow-file- access-from-files
exit
I assume you are making a AJAX call to another domain file for which you have to use JSONP as your dataType. Doing this you then dont have to make async = false.
Try this:
before you do the ajax call set display to block, after the ajax call(success or error doesnt matter) - just before you return - set display to none. You dont need displayLoadingImg and the code to handle it. Or do I miss ssomething?
We have a webpage using this code :
var xhr = new window.XMLHttpRequest();
// XHR for Chrome/Firefox/Opera/Safari.
// Create the XHR object.
xhr.open("GET","prp.xml", false);
xhr.send();
xmlDoc=xhr.responseXML;
It opens an XMLHttpRequest to get our XML file and then drops the result into the xmlDoc variable. I would like to know if it's possible to do that same operation using jQuery v1.11.0 and that the xmlDoc variable will still be usable by the rest of the code (aka still compatible with regular JS).
Depending on the user in here, people are using Firefox, Chrome, internet explorer 8, 9 or 10. I have read that internet explorer under 10 cannot use XMLHttpRequest, and i am pretty bad/lost with this whole jQuery thingy.
Thanks!
I'm not sure how regular JS handles XML, but an Ajax-call via jQuery is as simple as:
var xhr_variable;
$.ajax({
url: "your.xml",
success: function(data) {
console.log("working");
xhr_variable = data;
}, error: function(err) {
console.log("not working");
}
});
If you are new to Ajax, I recommend using error to check if it works or not. If works without problem, you can use the following:
var xhr_variable;
$.ajax({
url: "miep.xml",
success: function(data) {
xhr_variable = data;
}
});
Your XML-file will be assigned to your variable (if used in the correct scope).
Please note that for security reasons Ajax will only work if you call documents that are hosted on the same domain. (example.com can get documents for example.com, but not from example2.com).
I have searched relentlessly but just can't figure this one out. Why will this XHR connection work perfectly fine in Firefox but breaks in Chrome? I'm using this in conjunction with AngularJS, by the way.
$scope.upload = function(project, file) {
var formData = new FormData(); //Not really sure why we have to use FormData(). Oh yeah, browsers suck.
formData.append('', file.file); //The real file object is stored in this file container
file.xhr = new XMLHttpRequest();
file.xhr.open('PUT', '/api/projects/'+project._id+'/files', true);
//Progress event listener
file.xhr.upload.onprogress = function(event) {
if(event.lengthComputable) {
file.uploadPercent = Math.round(event.loaded / event.total * 100);
}
};
//Upload complete listener
file.xhr.upload.onload = function(event) {
file.uploaded = true;
};
//Every time the status changes
file.xhr.onreadystatechange = function(event) {
if(event.target.readyState == 4) {
//The file has been added, so tag the ID onto the file object
console.log(event.target.responseText);
file._id = JSON.parse(event.target.responseText)._id;
} else {
return;
}
};
file.xhr.send(formData);
};
In Firefox, the file is sent just fine to my server, and the responseText is returned exactly like I'd expect. However, in Chrome, I get this error: Error: INVALID_STATE_ERR: DOM Exception 11
Error: An attempt was made to use an object that is not, or is no longer, usable., which would be more helpful if it told me exactly what object was attempted to be used. I've read here that I should try to set async to false and use onreadystatechange, but I'm not sure how that helps, since I'm already using onreadystatechange.
Bug from 2009: XMLHttpRequest doesn't work while submitting a form - https://bugs.webkit.org/show_bug.cgi?id=23933