Checking if the upload process has been completed using XMLHttpRequest Level 2

Currently, I am utilizing ajax for uploading files. Once the file has been uploaded, PHP needs to conduct a thorough check on it (including mime type, size, virus scan using clamscan, and more). This process can take a few seconds, especially for larger files. While the file is being uploaded, an HTML5 <progress> bar fills up. However, once PHP begins its verification process, the progress should switch to "indeterminate." I have explored two possible solutions to achieve this transition, but unfortunately, both have not yielded the desired results:

Solution 1: Monitoring upload.onload event

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

This approach proved ineffective as the onload event triggers when the request is ready, rather than when the upload process is complete.

Solution 2: Verifying if upload progress percentage equals 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 + "%");
        }
   }
}
});

Unfortunately, the second solution also fell short because there were instances where the upload progress would halt around 97%, even though the file was fully uploaded and PHP had commenced handling it.

Could you suggest any alternative approaches for addressing this issue?

Answer №1

If you're looking to track the progress of an XHR object, make sure to listen for the readystatechange event on the XHR object itself, not on XHR.upload. When readyState reaches 4, it means the upload has finished sending data and the server has closed the connection. On the other hand, events like loadend and load trigger when the upload completes, regardless of whether the server closes the connection. Here's a breakdown of the different events you can monitor and when they occur:

    var xhr = new XMLHttpRequest();

    // ...
    // perform actions with xhr
    // ...

    xhr.upload.addEventListener('loadstart', function(e) {
      // Triggered when the request starts.
    });
    xhr.upload.addEventListener('progress', function(e) {
      // Fired while data is being sent and loaded.
    });
    xhr.upload.addEventListener('load', function(e) {
      // Indicates that the request has been successfully completed,
      // even if the server hasn't acknowledged it yet.
    });
    xhr.upload.addEventListener('loadend', function(e) {
      // Signifies the completion of the request (whether successful or failed).
      // Similar to 'load,' even without confirmation from the server.
    });
    xhr.upload.addEventListener('error', function(e) {
      // Occurs when the request fails.
    });
    xhr.upload.addEventListener('abort', function(e) {
      // Triggers when the request is aborted, e.g., via the abort() method.
    });
    xhr.upload.addEventListener('timeout', function(e) {
      // Activated when the specified timeout elapses before request completion.
    });

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

Answer №2

Referencing this bug report.

Let's dive into a comprehensive 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))
        // perform additional actions 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';

In the first PHP file, you will observe the progress as follows: 10%... 50%... 75%... 'perform additional actions' in Firefox (4/10/28/32) and IE (10/11). However, in Chrome/Chromium (33/37) and Opera (24), you will see: 10%... 50%... 75%... 100%... 'perform additional actions'.

// 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';

In the second PHP file, the progression will be: 10%... 50%... 75%... 100%... 'perform additional actions' across various browsers including Chrome/Chromium (33/37/53), Opera (24/42), Firefox (4/10/28/32/45), IE (10/11), and Edge (14)!

Answer №3

This particular drawback of the HTML5 specification is quite well known, as it could have easily been expanded to include additional information like timeRemaining and transferSpeed.

Have you thought about utilizing Math.round instead of Math.ceil for the percent variable? This way, a slight margin of error can be introduced to account for any discrepancies in percentage points.

If you find the UI getting stuck at less than 100% even though the backend process is complete, consider adding another listener for loadComplete:

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

Answer №4

Verify the readyState status by using

if(readyState==4) {//it's done, write your code here}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The contents of the image are currently unable to be viewed

I have a Blob of an image that I am trying to display in the browser. However, when I use the code below to open the image, it shows some Mandarin scripts on the window. Here is the code snippet: var url="http://....link..../downloadFile?fdsFileId="+file ...

Tips on displaying a spinner only when data is retrieved from an Http service

How can I ensure that a spinner is only shown during an HTTP service call and dismissed when my component receives data? To address this issue, I implemented a cache service to store data fetched from the HTTP service for future use. However, I want to sh ...

Encountering a 500 internal server error with Laravel 5.6 and Jquery Ajax

I keep encountering a 500 internal server error on a student portal that I am developing. Whenever I attempt to post a program using a POST request, the error occurs. Strangely, the same code works perfectly fine when posting the academic year. I am curren ...

Exploring the world of cybersecurity using ASP.NET MVC 4 Web API

After creating my first MVC 4 project using the web API template, I implemented CORS to restrict access to my API from external websites. Utilizing AJAX, I successfully retrieve data from the API to populate a webpage. However, I am concerned about prevent ...

React is producing a collection of <td>'s

My React code is very straightforward and it runs smoothly: function Columns(){ return ( <React.Fragment> <li>Hello</li> <li>World</li> </React.Fragment> ); } function Example(){ ...

Arranging an array based on numerical values embedded in strings

My array is as follows: var arr = [ '4msterdam', 'Par1s', 'N3w York', '2urich']; Is there a way to sort the elements of this array based on the numbers included in each string? ...

Unable to retrieve the value from a textarea when using Shopify Product Options by Bold

I'm currently facing an issue trying to retrieve the value of a textarea using Shopify's Product Options by Bold. The code works fine locally, but when I transfer it over to Shopify, I am unable to get the value. Despite looking at various resour ...

In the realm of JavaScript, what happens when a function yields yet another function while also welcoming another function as an argument?

After following a Node & Express project tutorial on YouTube, I encountered the following code snippet in an async JavaScript file: const asyncHWrapper = (fn) => { return async (req, res, next) => { try { await fn(req, res, next); } c ...

I'm working on separating the functionality to edit and delete entries on my CRM model, but I'm having trouble finding a way to connect these buttons with my data fields

I am encountering some difficulties while trying to implement separate functionality for editing and deleting items on my CRM model. I have already created the necessary API in Angular, but I am struggling to bind these buttons with my field. Any assistanc ...

Guide on converting HTML datetime picker datetime-local to moment format

I am looking to convert an input type : <input type="datetime-local" name="sdTime" id="stTimeID" onChange={this.stDateTime} /> into a specific date format: const dateFormat = 'MM/DD/YYYY hh:mm:ss a'; To achieve this, I need to transfer ...

Develop a fresh behavior on-the-fly

Here is the HTML code snippet: <div class="bold knowmore login" id="j-6"> <span>...</span> </div> and this jQuery script: $(function(){ $(".login").on("click", function(){ console.log('login clicked!'); $(" ...

What is the most effective way to save and access British pound symbols?

Every so often, I face this issue where I end up resorting to a messy workaround. There must be a correct way to handle it though, as it's common to work with UK pound symbols. The dilemma is this: when the user inputs a UK pound symbol (£) into a t ...

Using JavaScript, adding an element to an array results in the array returning a value of 1

When I try to push a string into an array and then return the array, I am getting unexpected result of "1". Upon closer inspection, it seems that the array is being successfully created but before returning it, only "1" is being returned. Here is the snip ...

Is there a way to execute .jsx Photoshop scripts on images using Java?

Currently, I am in the process of developing a Java program to apply edits to a sequence of images. However, I am searching for a simple and adaptable method to conduct these edits by utilizing Image Editors Scripts (such as Photoshop Scripts, Gimp Scripts ...

The Java Servlet change did not trigger an update in the web content

While using Eclipse to develop a website where Servlet sends data to JSP, I encountered an issue. Despite modifying the data in the Servlet, it continued to send outdated information to the JSP page. I attempted the following options: Menu - Project - cle ...

Populate Vue components dynamically without the need for additional frameworks like Nuxt in Vue3

My goal is to develop a checksheet application using Vue, where the tasks are specified in an excel file. ===== To start, task A needs to be completed. Followed by task B. and so on... ===== I am considering creating a CheckItem.vue component fo ...

Customizing the Steps Component in Ant Design

Currently, I am utilizing the Steps component from Ant Design. My goal is to enhance its functionality by adding additional components, like a button placed next to each step's description to make it more interactive. Furthermore, I am looking to inc ...

Switching up the content of an HTML page with JavaScript or JQuery: what you need

Is it possible to update HTML content using JavaScript or JQuery? https://i.sstatic.net/EWOXg.png I am trying to change the contents from 1 to 5 in a sequential order based on the time shown in the image. How can I achieve this using JavaScript or JQuery ...

Restricting the number of times a user can click on

I am currently displaying a table with data obtained from a database query. The structure of the table is outlined below: <table id="dt-inventory-list" class="table table-responsive"> <thead> <tr> <th>Field ...

Child functional component does not refresh after its props are altered by its parent component

Looking at the following code: MainParentComponent.js let data = 0; function MainParentComponent() { function handleClick() { data++; } return (<> <button onClick={handleClick}>Increase</button> < ...