1

All,

I have built a form, if the users fills in the form, my code can determine what (large) files need to be downloaded by that user. I display each file with a download button, and I want to show the status of the download of that file next to the button (download in progress, cancelled/aborted or completed). Because the files are large (200 MB+), I use a well-known function to read the file to be downloaded in chuncks. My idea was to log the reading of each (100 kbyte) chunk in a database, so I can keep track of the progress of the download. My code is below:

    function readfile_chunked($filename, $retbytes = TRUE) 
    {
          // Read the personID from the cookie 
          $PersonID=$_COOKIE['PERSONID'];
          // Setup connection to database for logging download progress per file
          include "config.php";
          $SqlConnectionInfo= array    ( "UID"=>$SqlServerUser,"PWD"=>$SqlServerPass,"Database"=>$SqlServerDatabase);
          $SqlConnection= sqlsrv_connect($SqlServer,$SqlConnectionInfo);
          ChunckSize=100*1024; // 100 kbyte buffer
          $buffer = '';
          $cnt =0;
          $handle = fopen($filename, 'rb');
          if ($handle === false) 
          {
                return false;
          }
          $BytesProcessed=0;
          while (!feof($handle)) 
          {
                 $buffer = fread($handle, $ChunckSize);
                 $BytesSent = $BytesSent + $ChunckSize;
                 // update database
                 $Query= "UPDATE [SoftwareDownload].[dbo].[DownloadProgress] SET     LastTimeStamp = '" . time() . "' WHERE PersonID='$PersonID' AND FileName='$filename'";
                 $QueryResult = sqlsrv_query($SqlConnection, $Query);
                 echo $buffer;
                 ob_flush();
                 flush();
                 if ($retbytes) 
                 {
                        $cnt += strlen($buffer);
                 }
          }
          $status = fclose($handle);
          if ($retbytes && $status) 
          {
                 return $cnt; // return num. bytes delivered like readfile() does.
          }
          return $status;
    }

The weird thing is that my download runs smoothly at a constant rate, but my database gets updated in spikes: sometimes I see a couple of dozen of database updates (so a couple of dozens of 100 kilobyte blocks) within a second, the next moment I see no database updates (so no 100 k blocks being fed by PHP) for up to a minute (this timeout depends on the bandwidth available for the download). The fact that no progress is written for a minute makes my code believe that the download was aborted. In the meantime I see the memory usage of IIS increasing, so I get the idea that IIS buffers the chuncks of data that PHP delivers. My webserver is running Windows 2008 R2 datacenter (64 bits), IIS 7.5 and PHP 5.3.8 in FastCGI mode. Is there anything I can do (in either my code, in my PHP config or in IIS config) to prevent IIS from caching the data that PHP delivers, or is there any way to see in PHP if the data generated was actually delivered to the downloading client? Thanks in advance!

  • Getting progress from download with PHP is not possible :) – OptimusCrime Apr 13 '12 at 09:37
  • @OptimusCrime: Wrong. Use a database to write the status, and read it with Ajax. Maybe use APC. – Madara's Ghost Apr 13 '12 at 09:41
  • @OptimusCrime - it is if you do what the OP is doing, by breaking the file into separate parts, and logging when each part is delivered – BenOfTheNorth Apr 13 '12 at 09:41
  • Related: http://stackoverflow.com/questions/7049303/show-progress-for-php-long-script – Madara's Ghost Apr 13 '12 at 09:42
  • Thanks all, but what Ben says is exactly what my question is about: I break the file in parts, and write to a database when a part is delivered. However, because IIS sits in between, PHP deliveres the file parts to IIS and not to the client. I'd like to know how to get around the IIS cache, so my parts are delivered in a more fluent way (and not the "let's-push-100-megabyte-in-a-buffer-and-wait-a-minute-for-it-to-clear" way as it is happening now). – Martijn Balink Apr 13 '12 at 12:00

2 Answers2

1

As no usefull answers came up, I've created a real dodgy workaround by using Apache strictly for the download of the file(s). The whole form, CSS etc is still delivered through IIS, just the downloads links are server by Apache (a reverse proxy server sits in between, so users don't have to fiddle with port numbers, the proxy takes care of this). Apache doesn't cache PHP output, so the timestamps I get in my database are reliable.

0

I've asked a similar question once, and got very good results with the answers there.

Basically, you use a APC cache to cache a variable, then retrieve it in a different page with an Ajax call from the client.

Another possibility is to do the same with a database. Write the progress to a database and have the client to test for that (let's say every second) using an Ajax call.

Community
  • 1
  • 1
Madara's Ghost
  • 172,118
  • 50
  • 264
  • 308