Monitoring File Uploads using Ajax and PHP
Creating the PHP FileUploader Class
Now that we have an understanding of how to use upload monitoring with APC, we can create a new PHP class to handle file uploads and to return this upload information. In the coming sections we will write code to interface this class, but for now we will focus on the development of the class.
We are going to use PHP sessions to store upload information. The reason we do this is so the script will complete correctly even if APC upload monitoring is not enabled. When we develop the Ajax part of this solution, the status monitoring script will be continually polling the status script. Once the upload has completed we need this loop to finish. For now though, let's focus on developing the PHP class.
The code created in this section will be broken down by each method. The full FileUploader.php file can be downloaded from the article file list.
Initializing FileUploader
The first thing we do is to create the class constructor. Because this solution uses sessions, we call session_start() in the constructor. This isn't necessarily an ideal solution: your application may already initiate sessions but we just do it here so the code for this article is all self-contained.
Here is the code to define the class, as well as its constructor.
class FileUploader { const SESSION_KEY = '__upload_status'; const ID_KEY = 'APC_UPLOAD_PROGRESS'; public function __construct() { session_start(); if (!array_key_exists(self::SESSION_KEY, $_SESSION)) { $_SESSION[self::SESSION_KEY] = array(); } } // ... other code }
We are going to store upload status session data as an array, with each element corresponding to a single file upload. We will store this data in a single element in the $_SESSION array. We define the name of this using the SESSION_KEY constant. Doing this using a constant allows us to easily change the name of this as required in the future without having to update all points in the code.
Additionally, we define the ID_KEY constant, which refers to the name of the form element that APC looks for in order to define an upload's ID. Once again this is unlikely to change but makes for cleaner code.
Checking for APC Upload Monitoring Support
The next step is to determine whether or not we have the ability to monitor the upload status. Doing this ensures that our code will work on all platforms regardless of whether APC is installed. For APC upload monitoring to work the following conditions must be met:
- The APC module must be loaded
- The apc_fetch() function must exist (which it should if APC is loaded, but we'll double check anyway).
- APC must be enabled (upload monitoring is disabled if APC is disabled, although this wasn't the case in APC 3.0.13).
- The
apc.rfc1867setting must be enabled.
In order to check each of these conditions we can use the following code:
class FileUploader { // ... other code public static function CanGetUploadStatus() { if (!extension_loaded('apc')) return false; if (!function_exists('apc_fetch')) return false; return ini_get('apc.enabled') && ini_get('apc.rfc1867'); } // ... other code }
Retrieving the Upload Status for a File
Next we implement a method to retrieve the upload status for a given file ID. This method works first by checking for existing status data in the session. If there isn't, the data is initialized. Next the code calls apc_fetch() to retrieve the upload information as described in the previous section. The session is then updated and the data is returned.
This code as described is shown below. The comments describe exactly what is occurring in the getUploadStatus() method.
class FileUploader { // ... other code public function getUploadStatus($id) { // sanitize the ID value $id = preg_replace('/[^a-z0-9]/i', '', $id); if (strlen($id) == 0) return; // ensure the uploaded status data exists in the session if (!array_key_exists($id, $_SESSION[self::SESSION_KEY])) { $_SESSION[self::SESSION_KEY][$id] = array( 'id' => $id, 'finished' => false, 'percent' => 0, 'total' => 0, 'complete' => 0 ); } // retrieve the data from the session so it can be updated and returned $ret = $_SESSION[self::SESSION_KEY][$id]; // if we can't retrieve the status or the upload has finished just return if (!self::CanGetUploadStatus() || $ret['finished']) return $ret; // retrieve the upload data from APC $status = apc_fetch('upload_' . $id); // false is returned if the data isn't found if ($status) { $ret['finished'] = (bool) $status['done']; $ret['total'] = $status['total']; $ret['complete'] = $status['current']; // calculate the completed percentage if ($ret['total'] > 0) $ret['percent'] = $ret['complete'] / $ret['total'] * 100; // write the changed data back to the session $_SESSION[self::SESSION_KEY][$id] = $ret; } return $ret; } // ... other code }
Handling File Uploads
To complete the FileUploader PHP class we will implement a method for actually handling the upload web form. Since this tutorial isn't focus specifically on how to handle file uploads the code has been somewhat simplified, but in essence this method will simply save an uploaded file to the specified location.
In addition to saving the file to the file system, this method will also update the upload status data in the session to indicate that the file upload has complete. If you refer to the getUploadStatus() method, there is a line of code that checks the finished array key. We use this to help the Ajax portion of our code know when to stop polling for the upload status.
class FileUploader { // ... other code public function upload($key, $path) { // ensure the given file has been uploaded if (!isset($_FILES[$key]) || !is_array($_FILES[$key])) return false; $file = $_FILES[$key]; $id = $_POST[self::ID_KEY]; // only proceed if no errors have occurred if ($file['error'] != UPLOAD_ERR_OK) return false; // write the uploaded file to the filesystem $fullpath = sprintf('%s/%s', $path, basename($file['name'])); if (!move_uploaded_file($file['tmp_name'], $fullpath)) return false; // update the session data to indicate the upload has completed $size = filesize($fullpath); $_SESSION[self::SESSION_KEY][$id] = array( 'id' => $id, 'finished' => true, 'percent' => 100, 'total' => $size, 'complete' => $size ); return true; } }
In order to focus specifically on monitoring upload status, the error handling in this code has been somewhat simplified. Additionally, the code described here doesn’t deal with duplicate files or do any checking on the type of file.
This completes the FileUploader.php file. You can download the complete file from the article file listing.



