PHP upload progress bar

7 Apr 2010 | Posted by: Paul Whittington
PHP upload progress bar

Creating am upload progress bar in PHP is far more difficult than you would initially expect. The first probelm is PHP versions before version 5.2 don't provide the required 'hooks' required to make this information available on your webpage. The second problem is making the whole thing work which requires the use of AJAX and a bit of creative thinking to use an iframe as the target of the form so that the javascript ajax routines can continue to run once the form has been submitted.

Step 1: Make sure your server is set up properly

<?php phpinfo(); exit; ?>

Put the code above at the top of one of your pages and call that page in a browser. At the very top you should see the installed PHP version. This needs to be greater than 5.2. You then need to make sure that APC (Alternative PHP Cache) is installed and theapc.rfc1867 setting is switched on. If APC is installed the phpinfo screen will have a seperate section called 'apc' and in that section you should see the setting for APC.rfc1867 and whether it is on or not. If any of these things are incorrect you will need to modify the server configuration or like me get the server support staff to do it for you. 

Step 2: Build the form

The form will look something like this

<?php $uploadKey = date('YmdHis');?>
<div id="uploadForm">
  <form name="uploadForm" id="uploadForm" action="/formtarget.php" method="POST" enctype="multipart/form-data" target="uploadIframe">
    <input type="hidden" name="APC_UPLOAD_PROGRESS" id="progress_key" value="<?php print $uploadKey;?>"/>
    <input type="file" name="uploadedFile"/><br />
    <input type="submit" onclick="startProgress('<?php print $uploadKey;?>'');" value="UPLOAD" />
  </form>
</div>
<div id="progressBar"></div>
<iframe id="uploadIframe" name="uploadIframe" style="display:none"></iframe>

 its a fairly standard form except

  • it submits to an iframe this is necessary so that the javascript (AJAX) can still run on the top level page refreshing the progress bar. When you have processed the form in the catching page in the iframe you'll need to do a javascript comand parent.location.href or similar.
  • There is a special hidden form field called 'APC_UPLOAD_PROGRESS' with a unique value. that you then pass to the progress script so that it can identify which file we are getting the server to tell us the progress of. This hidden field has to be before the file type field in the form.
  • There a div that I called progressBar that we will display the progress bar in
  • theres an onclick event on the submit button that we will use to start the uploader, we'll get to the javascript bit in a second

Step 3: create the graphics and CSS

Two basic classes a grey <div> with a red div inside it, we're going to set the the width of the inner one as percentage of the outer using the percentage passed back from the upload script.. I had these in my common CSS file though you could have these in the <head>. I did have some background images in here to make it a bit nicer but have kept it simple for this example.

.progressGrey {
    position:relative;
    width: 300px;
    padding:1px 0px 1px 3px;
    background: #ccc;
    color: #666;
    font-size: 9px;
    border:1px solid #000;
    overflow:hidden;
    white-space:nowrap;
    font-weight:bold
}
.progressRed {
    position:absolute;
    top:0px;
    padding:1px 0px 1px 3px;
    left:0px;
    height: 15px;
    background: #f00;
    color: #fff;
    font-size: 9px;
    border-right:1px solid #000;
    overflow:hidden;
    white-space:nowrap;
    font-weight:bold
}

Step 4: create the PHP script that reports the upload progress

<?php
   $uploadKey=$_GET["uploadKey"];
   $status = false;
   $percentage = 0;
   $result = "INITIALISING UPLOAD";
   if (function_exists('apc_fetch')) {
      $status = apc_fetch('upload_'.$uploadKey);
   }
   if (is_array($status)) {
      if (array_key_exists("total", $status)&&array_key_exists("current", $status)) {       
         $percentage = (int)(($status['current'] / $status['total']) * 100);
         if ($status['current']>=$status['total']) {
            $percentage = 100;
            $result = "UPLOAD COMPLETE";
         } else {
             $bytes = array('B','KB','MB','GB','TB');
             foreach($bytes as $val) {
                 if($status["total"]  > 1024){
                     $status["total"] = $status["total"] / 1024;
                     $status["current"] = $status["current"] / 1024;
                 } else {
                      break;
                 }
             }
             $result =  $percentage."% (".round($status["current"], 2)." ";
             $result.= $val." of ".round($status["total"], 2)." ".$val.")";
         }
      }
   }
   header ("content-type: text/xml");
   print "<?xml version="1.0" encoding="utf-8"?>";
?>
<progress percentage="<?php print $percentage;?>" result="<?php print $result;?>"></progress>

Hopefully this should be fairly self-explanatory...

I called this script progress.php and kept in the same directory as the php file that calls it. We're going to call it in the next step in an AJAX command passing the unique upload key to it in a querystring /progress.php?uploadKey=xxxxxx This file will check the progress of the upload with the server and return the result back to the AJAX script as an XML file. We'll check for errors as we go and return some generic values in the event of an unexpected result. I'm going to pass only 2 values back to the AJAX script: an integer with the percentage of the progress and a pre formatted message containing a description of the upload.

step 5: Create the AJAX script to load and report the progress

This needs to sit in a javascript file, you can either have it inline in the page that holds the form or link to it in an external .js file.

var XMLHttpArray = [
    function() {return new XMLHttpRequest()},
    function() {return new ActiveXObject("Msxml2.XMLHTTP")},
    function() {return new ActiveXObject("Msxml2.XMLHTTP")},
    function() {return new ActiveXObject("Microsoft.XMLHTTP")}
];
var theUploadKey = "";
function createXMLHTTPObject(){
    var xmlhttp = false;
    for(var i=0; i<XMLHttpArray.length; i++){
        try{xmlhttp = XMLHttpArray[i]();}
        catch(e){continue;                        }
        break;
    }
    return xmlhttp;
}
function AjaxRequest(url,callback,method){
    var req = createXMLHTTPObject();
    req.onreadystatechange= function(){
        if(req.readyState != 4) {return;}
        if(req.status != 200) {return;}
        callback(req);
    }
    req.open(method,url,true);
    req.setRequestHeader('User-Agent', 'My XMLHTTP Agent');
    req.send(null);
}
function startProgress(uploadKey) {
    theUploadKey = uploadKey;
    document.getElementById("progressBar").style.display="block";
    document.getElementById("progressBar").innerHTML = "<h3>Upload progress...</h3><div class='progressGrey'>&nbsp;</div>";
    document.getElementById("uploadForm").style.display="none";
    getProgress(uploadKey);
}   

function getProgress(uploadKey){
AjaxRequest("/progress.php?uploadKey="+uploadKey,updateProgress,"get");
}
function updateProgress(req){
    var respXML=req.responseXML;
    if(!respXML) {
        return;
    }
    var result=respXML.getElementsByTagName('progress')[0].getAttribute('result');
    var percentage=respXML.getElementsByTagName('progress')[0].getAttribute('percentage');
    if (percentage>0) {
        document.getElementById("progressBar").innerHTML = "<h3>Upload progress...</h3><div class='progressGrey'>"+result+"<div class='progressRed' style='width:"+percentage+"%'>"+result+"</div></div>";
    } else {
        document.getElementById("progressBar").innerHTML = "<h3>Upload progress...</h3><div class='progressGrey'>"+result+"</div>";
    }
    if (percentage<100) {
        var timeoutID = window.setTimeout("getProgress(theUploadKey)", 1000);
    }
}

 


 

Please rate this article

Click the stars below to give this article a mark out of 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 5 / 10


Related Articles

Creating a conditional CSS with PHP
9 Mar 2011 | Posted by: Paul Whittington
Creating a conditional CSS file, mainly to avoid the issues of IE6 but also to allow easier modifications to mobile device stylesheets.
73% rating, 4 comments

How to embed CSS cross browser fonts using CSS3
7 Jun 2011 | Posted by: Keir Cleland
Being able to embed whichever font you wish is the dream of web developers everywhere. No more time consuming excursions into photoshop to cut up page titles or navagation elements.
73% rating, 4 comments

php prepared (parameterized) statements into arrays
7 Sep 2011 | Posted by: Paul Whittington
A couple of functions to get data from a database into an associative array using prepared or parametized queries, that therefore overcome the danger of sql injection.
73% rating, 4 comments



Post your comments...

We would really appreciate any comments or additions that you have. Include a link in your comment and if we think your comment is appropriate we will publish it. If found this article in any way useful we would really appreciate you bookmarking the page with any of the social bookmarking links provided.

Hi,it not working on my server and i dont know why...
Posted By: Mfws.Ro - 21 Jun 2010
There are so many things that could be wrong without a link or something I wouldn't know where to start. Likely reasons are the paths to the certain files, probrably where the javascript calls the AJAX command. As with anything javascript based open the page in Firefox or Chrome and watch for any errors that appear in the error console.
Posted By: Paul
Thanks for posting these instructions itssaved me a lot of time Dave
Posted By: Dave - 24 Jul 2010
would have been nice to provide a working download example insteadof bits of code : /
Posted By: jay - 25 Sep 2011


Name:
(optional, shown on site)
Email:
(optional, never shown on site)
Code:
(case sensitive)
captcha
Your feedback: