44

I'm using XmlHttpRequests to upload images to a server and I'd like to show the user the progress of these uploads.

Unfortunately the interval between calls to my onprogress-event handler is too large. Usually onprogress is called only once or twice for a 500k image.

Here is my code:

/* This function is not called often enough */
function progress(e){
    console.log('Uploading: ' + Math.round((e.loaded / e.total) * 100) + ' %');
}

var xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', progress, false);
xhr.send(data);

Can this behaviour be changed or is this hardcoded somewhere in the browser implementation?

Oscar
  • 766
  • 1
  • 6
  • 13
  • How about this one? [How to get progress from XMLHttpRequest](http://stackoverflow.com/questions/76976/how-to-get-progress-from-xmlhttprequest) – mplungjan Mar 31 '11 at 06:07
  • @mplungjan My problem is not getting the progress. My problem is how often my event handler is called. I've updated my question to clarify. – Oscar Mar 31 '11 at 07:50
  • @Oscar - I realise that, however the thread I posted has a lot of examples of various ways – mplungjan Mar 31 '11 at 08:17
  • I haven't tested it but there is a jQuery plugin which uses the PHP uploadprogress module which can show the progress as well. Perhaps it helps: http://nixboxdesigns.com/demos/jquery-uploadprogress.php. I guess it will become a little complicated but it might be worth a try. – pimvdb Mar 31 '11 at 15:02
  • What browsers have you tested this in? – Andrew Marsh Apr 04 '11 at 12:32
  • @Andrew Firefox 4, Chrome 10+ and Safari 5. – Oscar Apr 04 '11 at 13:08
  • I wish that this event fired more often :( Awesome question +1. – www139 Dec 26 '15 at 14:20
  • I think your best bet would be to use CSS transitions for the width of a progress bar and just animate the percentage width of the progress bar. Transitions will make it more "real". – www139 Dec 26 '15 at 14:22

2 Answers2

41

The W3 sets forth the following guidelines in their XMLHttpRequest Level 2 document. Obviously varying levels of conformance across browsers are to be expected.

Uploads:

While the request entity body is being uploaded and the upload complete flag is false, queue a task to fire a progress event named progress at the XMLHttpRequestUpload object about every 50ms or for every byte transmitted, whichever is least frequent. - W3 XMLHttpRequest Level 2 (Bolded for emphasis)

Downloads:

When it is said to make progress notifications, while the download is progressing, queue a task to fire a progress event named progress about every 50ms or for every byte received, whichever is least frequent. - W3 XMLHttpRequest Level 2 (Bolded for emphasis)

I am not aware of an api to customize this functionality.

Matt Bierner
  • 58,117
  • 21
  • 175
  • 206
  • 3
    Thank you. For some reason it never occured to me to check the specs. – Oscar Apr 05 '11 at 04:04
  • 1
    Having tried in 3 different browsers, it seems that the progress event is not triggered until atleast 69 bytes of data have been received from the server. After that, it can be triggered with each additional byte. It seems like the browsers behave according to this standard, but only after receiving the first 69 bytes. – Sam Bull Jan 18 '17 at 17:55
  • @SamBull That's good to know! I've been wracking my brain trying to figure out why my 10, 9, 8, ... countdown test isn't firing any progress indicators until it's completely done! – Michael May 25 '17 at 22:23
  • @SamBull Hmm, unfortunately even if I send 100 bytes and then one byte every second for 10 seconds, I *still* don't get any progress indicators until everything has been transferred! – Michael May 25 '17 at 22:26
  • @Michael Well, it's either a works-for-me-not-for-you thing, or your code is incorrect. I've seen my code working on a number of other people's computers though. For the record, my testing was on Ubuntu with Firefox, Chromium and GNOME Web. I've also seen my code working on OS X with Chrome. One thing that might be worth checking, is how you are sending the data from the server (e.g. make sure it is not buffering output, which most programming languages will do by default). – Sam Bull Jun 03 '17 at 11:54
  • @SamBull I know it wasn't buffering because when I connected directly to the server via telnet I could see it outputting every second. In any case, I finally got it to work by upping the amount of bytes I sent... somewhere between 3k and 10k (depending on the browser) seemed to be required. – Michael Jun 03 '17 at 16:24
  • @Michael I've made a little test page, so you can verify if my code works for you. Note that I needed to add a "SetEnv no-gzip" to the server config to get it working correctly. http://sambull.org/javascript-test.html View source of the page to see the JS. – Sam Bull Jun 21 '17 at 11:42
  • @Michael There was also some caching issues, so I also needed to add to the server config: Header set Cache-Control "no-cache". – Sam Bull Jun 21 '17 at 11:56
  • 1
    OK, since testing that, in the past couple of weeks, it seems that Chrome has changed behaviour, and now requires 1024 bytes. I've adjusted that script, so you can see if your browser triggers at 69 or 1024. – Sam Bull Jul 07 '17 at 13:29
3

Also, be careful that local debugging HTTP proxies (like Charles, for instance) tend to affect progress events firing interval.

user574535
  • 81
  • 1
  • 2