08 May 2010

Tracking file upload progress in php

Tracking file upload progress

To receive a file, we must first set up a form which will receive the file. Conveniently, HTML comes with a standard field type for files. Like all HTML form fields, it is named logically, as type file It comes with a handy Browse button that appears to the right of the block by default.

HTML form for upload.php
<?php
$id = $_GET['id'];
?>

<form enctype="multipart/form-data" id="upload_form"
action="target.php" method="POST">

<input type="hidden" name="APC_UPLOAD_PROGRESS"
id="progress_key" value="<?php echo $id?>"/>

<input type="file" id="test_file" name="test_file"/><br/>

<input onclick="window.parent.startProgress(); return true;"
type="submit" value="Upload!"/>

</form>

We need to make a PHP page for this form because we need a unique key to track the upload. Ultimately, it will be part of the URL used to call this page as a GET value. This number is going to be the value for the APC cache entry key we will retrieve later. To pass that value, the form field needs a hidden field with a special name that will let APC know that it needs to store the file upload status. This field is called APC_UPLOAD_PROGRESS. This is the aforementioned hook that starts the caching process. To make sure PHP can access the correct item in the cache, we use the unique ID we retrieved as the value of the hidden field, thus creating a key of that value. Once the user submits the form -- we'll deal with the submit button shortly -- the browser sends the file and the key as part of the POST data sent to the server.


Catching the throw
When the file is included in the submitted form, it is sent to a temporary location on the server until it is saved to a permanent location. When it is in the temporary storage, it is available through the $_FILES  associative array. Using the file upload functions that are standard issue with PHP, you can select a path and save them to the server or handle them however you want.
The target.php file
<?php  

if($_SERVER['REQUEST_METHOD']=='POST') {
  move_uploaded_file($_FILES["test_file"]["tmp_name"], 
  "c:\\sw\\wamp\\www\\" . $_FILES["test_file"]["name"]);
  echo "
File uploaded.  Thank you!
";

}

?>


The getprogress.php file

Making progress



<?php
if(isset($_GET['progress_key'])) {


  $status = apc_fetch('upload_'.$_GET['progress_key']);
  echo $status['current']/$status['total']*100;


}
?>






              
JavaScript shows the bar
Now you just need a way to get the script to update the width not with an arbitrary number but with the completion percentage.

The main file, progress.php
Now you're ready to start building the actual progress bar. To keep things simple, the script uses CSS to create a div that emulates a bar and can be controlled using JavaScript,
              
<html>
<head><title>Upload Example</title></head>
<body>

<script type="text/javascript">

var counter = 0;

function startProgress(){
    document.getElementById("progressouter").style.display="block";
    fire();
}

function fire(){
   if (counter < 101){
     document.getElementById("progressinner").style.width =
                                                     counter+"%";
     counter++;
     setTimeout("fire()",100);
   }
}

</script>

<div id="progressouter" style=
    "width: 500px; height: 20px; border: 6px solid red; display:none;">
   <div id="progressinner" style=
       "position: relative; height: 20px; background-color: purple; width: 0%; ">
   </div>
</div>

<span onclick="startProgress()">Start me up!</span>

</body>
</html>


The final progress.php page

Putting it all together
All you've got left to do now is to hook everything together.



<?php
$id = uniqid("");
?>
<html>
<head><title>Upload Example</title></head>
<body>

<script src="http://maps.google.com/maps?file=api&v=2&key=<yourkeyhere>"
type="text/javascript"></script>

<script type="text/javascript">

function getProgress(){
GDownloadUrl("getprogress.php?progress_key=<?php echo($id)?>",
function(percent, responseCode) {
document.getElementById("progressinner").style.width = percent+"%";
if (percent < 100){
setTimeout("getProgress()", 100);
}
});

}

function startProgress(){
document.getElementById("progressouter").style.display="block";
setTimeout("getProgress()", 1000);
}

</script>

<iframe id="theframe" name="theframe"
src="upload.php?id=<?php echo($id) ?>"
style="border: none; height: 100px; width: 400px;" >
</iframe>
<br/><br/>

<div id="progressouter" style=
"width: 500px; height: 20px; border: 6px solid red; display:none;">
<div id="progressinner" style=
"position: relative; height: 20px; background-color: purple; width: 0%; ">
</div>
</div>

</body>
</html>

Starting at the bottom and working our way up, we've added an iframe that embeds the upload.php script,  feeding it the unique ID generated at the top of the page.


Now, remember the Submit button from that form?

That button does two things. It does submit the form, like a normal Submit button, but before it does that, it calls the startProgress() script in the main window. The startProgress() script tells the progress bar to display itself — it starts out with a display property of none —, then tells the browser to wait for one second before executing the getProgress() script.

 

1 comment: