XMLHttpRequest Level 2 - Determinate if upload finished

17,954

Solution 1

The event you want to listen to is readystatechange on the XHR object (not on XHR.upload). readyState is 4 when the upload has finished sending and the server closes the connection. loadend/load fire when the upload has finished regardless of whether the server closes the connection. Just for reference, here are the events you can listen to and when they fire:

    var xhr = new XMLHttpRequest();

    // ...
    // do stuff with xhr
    // ...

    xhr.upload.addEventListener('loadstart', function(e) {
      // When the request starts.
    });
    xhr.upload.addEventListener('progress', function(e) {
      // While sending and loading data.
    });
    xhr.upload.addEventListener('load', function(e) {
      // When the request has *successfully* completed.
      // Even if the server hasn't responded that it finished.
    });
    xhr.upload.addEventListener('loadend', function(e) {
      // When the request has completed (either in success or failure).
      // Just like 'load', even if the server hasn't 
      // responded that it finished processing the request.
    });
    xhr.upload.addEventListener('error', function(e) {
      // When the request has failed.
    });
    xhr.upload.addEventListener('abort', function(e) {
      // When the request has been aborted. 
      // For instance, by invoking the abort() method.
    });
    xhr.upload.addEventListener('timeout', function(e) {
      // When the author specified timeout has passed 
      // before the request could complete.
    });

    // notice that the event handler is on xhr and not xhr.upload
    xhr.addEventListener('readystatechange', function(e) {
      if( this.readyState === 4 ) {
        // the transfer has completed and the server closed the connection.
      }
    });

Solution 2

Based on https://bugzilla.mozilla.org/show_bug.cgi?id=637002.

Let's go for a complete working example...

// YOUR (SIMPLE) JAVASCRIPT FILE
var form = new FormData(), xhr = new XMLHttpRequest();
form.append('inputname', YOURFILE);

xhr.open('POST', 'http://oneserver/onephpfile', true);
xhr.setRequestHeader('X-CSRF-Token', 'somestring');
xhr.onreadystatechange = function () {
    if ((xhr.readyState === 4) && (xhr.status === 200))
        // do other thing with xhr.responseText.trim()
};

xhr.upload.addEventListener('loadstart', showProgressBarFunction, false);
xhr.upload.addEventListener('progress',  updateProgressBarFunction, false);
xhr.upload.addEventListener('load',      updateProgressBarFunction, false);
xhr.send(form);

// YOUR FIRST (SIMPLE) PHP FILE
header('Content-Type: text/plain; charset=utf-8');
header('Cache-Control: no-cache, must-revalidate');

sleep(20);
echo 'file processing ended';

With this first PHP file, you will see: 10%... 50%... 75%... 'do other thing' with Firefox (4/10/28/32) and IE (10/11). However you we will see: 10%... 50%... 75%... 100%... 'do other thing' with Chrome/Chromium (33/37) and Opera (24).

// YOUR SECOND (SIMPLE) PHP FILE
header('Content-Encoding: chunked', true);
header('Content-Type: text/plain; charset=utf-8');
header('Cache-Control: no-cache, must-revalidate');
ini_set('output_buffering', false);
ini_set('implicit_flush', true);
ob_implicit_flush(true);
for ($i = 0; $i < ob_get_level(); $i++)
    ob_end_clean();
echo ' ';

sleep(20);
echo 'file processing ended';

With this second PHP file, you will see: 10%... 50%... 75%... 100%... 'do other thing' with Chrome/Chromium (33/37/53), Opera (24/42), Firefox (4/10/28/32/45), IE (10/11) and Edge (14)!

Solution 3

This is a relatively known downfall of the hTML5 spec, when they could have easily extended it to add information such as timeRemaining and transferSpeed.

Have you considered using math.round instead of math.ceil for var percent so that you are baking in a bit of fuzziness that would help get around a few % points being off?

You should also add another listener for loadComplete, if you are getting the UI stuck at <100% even though it is complete on the backend:

//only fires once
xhr.addEventListener('loadend', uploadComplete, false);
function uploadComplete(event) {
    console.log('rejoice...for I have completed');
    //do stuff
}
Share:
17,954
ninov
Author by

ninov

Updated on July 10, 2022

Comments

  • ninov
    ninov almost 2 years

    I am using ajax for file uploads. After the file is uploaded, php should check it (mime, size, virus (clamscan) and more) - this takes some seconds for larger files. While the file is uploading, a HTML5 <progress> is filling, when the file is ready and PHP starts checking, the progress should switch to indeterminate. I thought of to ways to do this (which both do not work):

    Checking upload.onload event

    xhr.upload.addEventListener("load", function (e) {
        $("#uploadprogress").attr("value", false);
        $("#uploadprogress").attr("max", false);
        $("#progress").text("Checking file...");
    });
    

    This doesn't work, because the onload-event firest when the request is ready, not when upload is ready.

    Checking if upload progress percentage = 100%

    xhr.upload.addEventListener("progress", function (e) {
        if (e.lengthComputable && e) {
            p = (e.loaded / e.total);
            if (p==1) {
                $("#uploadprogress").attr("value", false);
                $("#uploadprogress").attr("max", false);
                $("#progress").text("Checking file...");
            } else {
                var percent = Math.ceil(p * 1000) / 10;
                $("#uploadprogress").val(e.loaded);
                $("#uploadprogress").attr("max", e.total);
                $("#progress").text("Uploading... " + percent + "%");
            }
       }
    }
    });
    

    This does not work, because the upload percentage sometimes stops at approx. 97%, despite the upload is finished and PHP starts handling the files

    Is there another possibility checking this?

  • ninov
    ninov about 11 years
    But I don't want to check if the connection is closed, because after the upload is finished, PHP checks the file, which takes approx. 10 seconds and returns with some JSON. When PHP begins checking the file (=when upload is finished), the progress bar should switch to indeterminated
  • zertosh
    zertosh about 11 years
    when the file finishes transferring, the loadend/load events fire, when your script finishes checking the file (which is when PHP closes the connection) the readystatechange event fires with readyState===4. so for your purposes, switch the progress bar to indeterminated at the readystatechange event. see: gist.github.com/anonymous/5208652
  • ninov
    ninov about 11 years
    Doesn't work. This is my JS: gist.github.com/anonymous/2a2fef447399a3b8e734 Imagine the PHP script is just sleep(10);. Now I want to show the indeterminated progress bar for this 10 seconds
  • zertosh
    zertosh about 11 years
    you might have a browser issue. i just tested it (with some syntax fixes, etc.) on Chrome 25.0.1364.172 and 27.0.1449.0 and it works as expected. However, FF 19.0.2 is firing load/loadend/readyState 4 simultaneously.
  • ninov
    ninov about 11 years
    Well, Firefox 19.0.2 is the browser I am using... I'll test with Chrome.
  • Gleno
    Gleno over 10 years
    Make sure to add the listener to the upload property and not xhr directly.