Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Show AJAX upload status on progress element

I have an AJAX script to upload a file to a PHP script that is likely to take at least 10 seconds to run. I would like to display progress for it for the user.

In the executing class, I have a property $progress which is updated with the progress (in 1-100), and a method get_progress() (which's purpose should be obvious).

The question is, how to update the <progress> element on the front end for the user to see?

I think AJAX is the solution, but I just can not get my head around it. I can not get to the same object instance.

like image 649
Madara's Ghost Avatar asked Aug 13 '11 08:08

Madara's Ghost


People also ask

How can I see the progress of Ajax call?

Use the ajaxStart to start your progress bar code. $(document). ajaxStop(function(){ // hide the progress bar $("#progressDialog"). modal('hide'); });


2 Answers

I'll put this here as a reference for anyone searching - this relies on no javascript at all..

<?php  /**  * Quick and easy progress script  * The script will slow iterate through an array and display progress as it goes.  */  #First progress $array1  = array(2, 4, 56, 3, 3); $current = 0;  foreach ($array1 as $element) {     $current++;     outputProgress($current, count($array1)); } echo "<br>";  #Second progress $array2  = array(2, 4, 66, 54); $current = 0;  foreach ($array2 as $element) {     $current++;     outputProgress($current, count($array2)); }  /**  * Output span with progress.  *  * @param $current integer Current progress out of total  * @param $total   integer Total steps required to complete  */ function outputProgress($current, $total) {     echo "<span style='position: absolute;z-index:$current;background:#FFF;'>" . round($current / $total * 100) . "% </span>";     myFlush();     sleep(1); }  /**  * Flush output buffer  */ function myFlush() {     echo(str_repeat(' ', 256));     if (@ob_get_contents()) {         @ob_end_flush();     }     flush(); }  ?> 
like image 93
l0ft13 Avatar answered Oct 14 '22 21:10

l0ft13


If your task is to upload a huge data-set or process it on the server, while updating progress to the server you should consider going with some sort of jobs architecture, where you initiate the job and do it with some other script running on the server (for example scaling / processing images etc). In this you do one thing at a time, thus forming a pipeline of tasks where there is an input and a final processed output.

At each step of pipeline the status of task is updated inside the database which can then be sent to the user by any server-push mechanism which exists these days. Running a single script which handles uploads and updates puts load on your server and also restricts you (what if the browser closes, what if some other error occurs). When process is divided into steps you can resume a failed task from the point where it succeeded last.

There exists many ways to do it. But the overall process flow looks like this

enter image description here

The following method is what I did for a personal project and this script held good for uploading and processing thousands of high resolution image to my server which then were scaled down into multiple versions and uploaded to amazon s3 while recognizing objects inside them. (My original code was in python)

Step 1 :

Initiate the transport or task

First Upload your content and then return a transaction id or uuid for this transaction immediately via a simple POST request. If you are doing multiple files or multiple things in the task, you may also want to handle that logic in this step

Step 2:

Do the job & Return the progress.

Once you have figured out how transactions occur you can then use any server side push technology to send update packet. I would choose WebSocket or Server Sent Events whichever applicable falling back to Long Polling on un-supported browsers. A simple SSE method would look like this.

function TrackProgress(upload_id){      var progress = document.getElementById(upload_id);     var source = new EventSource('/status/task/' + upload_id );      source.onmessage = function (event) {         var data = getData(event); // your custom method to get data, i am just using json here         progress.setAttribute('value', data.filesDone );         progress.setAttribute('max', data.filesTotal );         progress.setAttribute('min', 0);     }; }  request.post("/me/photos",{     files: files }).then(function(data){      return data.upload_id; }).then(TrackProgress); 

On the server side, you will need to create something which keeps a track of the tasks, a simple Jobs architecture with job_id and progress sent to the db shall suffice. I would leave the job scheduling to you and the routing as well, but after that the conceptual code (for simplest SSE which will suffice the above code) is as follows.

<?php header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); /* Other code to take care of how do you find how many files are left     this is really not required */ function sendStatusViaSSE($task_id){     $status = getStatus($task_id);     $json_payload = array('filesDone' => $status.files_done,                           'filesTotal' => $status.files_total);     echo 'data: ' . json_encode( $json_payload ) . '\n\n';     ob_flush();     flush();      // End of the game     if( $status.done ){         die();     }  }  while( True ){      sendStatusViaSSE( $request.$task_id );      sleep(4); }  ?> 

A good tutorial on SSE can be found here http://html5doctor.com/server-sent-events/

and you can read more about pushing updates from the server on this question Pushing updates from server

The above was a conceptual explanation, there are other ways to achieve this but this was the solution that took care of a fairly huge task in my case.

like image 28
ShrekOverflow Avatar answered Oct 14 '22 20:10

ShrekOverflow