Upload progress in sessions

File upload progress feedback is a requirement that has become more and more common, for example large email attachments. Before PHP 5.4, we could implement it through the functionality provided by APC. Or we could use the PECL extension uploadprogress. Although they can solve the problem well, they also have obvious shortcomings:

  • 1. They both require extra installation (we had no intention of adding APC to PHP 5.4)
  • 2. They both use a local mechanism to store this information: APC uses shared memory, while uploadprogress uses the filesystem (not counting NFS). This causes trouble when there are multiple front-end machines.

    From PHP’s point of view, the best place to store this information should be the SESSION. First, it is a mechanism natively supported by PHP. Second, it can be configured to be stored anywhere (supporting sharing across multiple machines). Precisely because of this, Arnaud Le Blanc proposed the RFC for reporting upload progress through Session, and the implementation is now included in the PHP 5.4 trunk. This new feature provides some new INI settings, which are very similar to the related APC settings:

  • session.upload_progress.enabled[=1] : whether to enable upload progress reporting (enabled by default)

  • session.upload_progress.cleanup[=1] : whether to delete the progress data promptly after the upload finishes (enabled by default, recommended).
  • session.upload_progress.prefix[=upload_progress_] : the progress data will be stored in _SESSION[session.upload_progress.prefix . _POST[session.upload_progress.name]]
  • session.upload_progress.name[=PHP_SESSION_UPLOAD_PROGRESS] : if _POST[session.upload_progress.name] is not set, progress will not be reported.
  • session.upload_progress.freq[=1%] : how often the progress is updated (number of bytes already processed); a percentage indicated by ‘%’ is also supported.
  • session.upload_progress.min_freq[=1.0] : the time interval for updating the progress (in seconds)

For the following upload form:

<form action="upload.php" method="POST" enctype="multipart/form-data">
 <input type="hidden"
     name="<?php echo ini_get("session.upload_progress.name"); ?>" value="laruence" />
 <input type="file" name="file1" />
 <input type="file" name="file2" />
 <input type="submit" />
</form>

If we upload a large enough file (even better if the network speed is slow enough :P), we can get progress information like the following from _SESSION:

 $_SESSION\["upload\_progress\_laruence"\] = array(
  "start_time" => 1234567890, // request time
  "content_length" => 57343257, // total size of the uploaded files
  "bytes_processed" => 453489, // size already processed
  "done" => false, // TRUE when all upload processing is finished
  "files" => array(
   0 => array(
    "field_name" => "file1", // name of the upload field in the form
    // The following 3 elements equals those in $_FILES
     "name" => "foo.avi",
     "tmp_name" => "/tmp/phpxxxxxx",
     "error" => 0,
     "done" => true, // becomes TRUE when this file is fully processed
     "start_time" => 1234567890, // time when processing of this file started
     "bytes_processed" => 57343250, // size of this file already processed
    ),
    // An other file, not finished uploading, in the same request
    1 => array(
     "field_name" => "file2",
     "name" => "bar.avi",
     "tmp_name" => NULL,
     "error" => 0,
     "done" => false,
     "start_time" => 1234567899,
     "bytes_processed" => 54554,
    ),
   )
  );

Isn’t this very convenient? But a reminder is still needed: PHP 5.4 is still in the development stage, and before the final release, any new feature may be adjusted or changed. If you have any suggestions, feedback is welcome as well, to help us make PHP better. Thank you