I wrote a password protected script in php. Once someone logs in they can either click on a link to view a video or click on a link to download the video. I need to be able to allow them to download the video in zip format.

I am trying to use some code from php.net and it doesnt seemt o work.

When fopen tries to open the file it fails. I am opening a zip file.

Please help!

// translate file name properly for Internet Explorer.
     if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE")){
          $fileName = preg_replace('/\./', '%2e', $fileName, substr_count($fileName, '.') - 1);
     }
     // make sure the file exists before sending headers
     if(!$fdl=@fopen($fileString,'r')){
          die("Cannot Open File!");
     }
	 else {
          header("Cache-Control: ");// leave blank to avoid IE errors
          header("Pragma: ");// leave blank to avoid IE errors
          header("Content-type: application/octet-stream");
          header("Content-Disposition: attachment; filename=\"".$fileName."\"");
          header("Content-length:".(string)(filesize($fileString)));
          sleep(1);
          fpassthru($fdl);
     }

Greg

    padanaram wrote:

    When fopen tries to open the file it fails

    And what error message does PHP give you to debug this failure? Oh, wait, I forgot, you're suppresing those messages.

    If you're debugging a problem with a script, don't suppress PHP error messages. Remove the error suppressor from the fopen() call and try it again. If it fails, PHP will output a message telling you why. If it doesn't, double check that error_reporting is set to E_ALL.

    Also, just out of curiosity, why do you have this:

    sleep(1);

    ?

      Aside to what brad said up there, just a note that fpassthru() slurps the file into PHP memory and then sends it back to client. If the file is bigger than current memory available for the process, it will crash.

        I have a few questions and need some help with the download script.

        1) I am using fpassthru because it allows me to store the file outside of the document root for security reasons. Is there another way to do this? The download is a video download for a rock band so the server might get slammed. Will I have a problem with fpassthru failing?

        2) When I go to the url of the download script I get a bunch of error messages saying that the headers cant be sent.

        Here is one of the error messages.

        Warning: Cannot modify header information - headers already sent by (output started at /var/www/html/include/error.inc:67) in /var/www/html/bestbuy/download.php on line 43

        3) Instead of the file downloading I get a bunch of binary characters in the browser.

        Here is my download script.

        $fileDir = $row['file_path']; // supply a path name.
             $fileName = $row['file_name']; // supply a file name.
             $fileString = $fileDir . '/' . $fileName; // combine the path and file
             // translate file name properly for Internet Explorer.
             if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE")){
                  $fileName = preg_replace('/\./', '%2e', $fileName, substr_count($fileName, '.') - 1);
             }
             // make sure the file exists before sending headers
             if(!$fdl=@fopen($fileString,'r')){
                  die("Cannot Open File!");
             } 
        	 else {
        	      //Close the session to allow for header() to be sent
                  session_write_close();
                  header("Cache-Control: ");// leave blank to avoid IE errors
                  header("Pragma: ");// leave blank to avoid IE errors
        		  header("Expires: 0"); 
                  header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); 
                  header("Content-Description: File Transfer");
                  header("Content-type: application/zip");
        		  header($header); 
                  header("Content-Transfer-Encoding: binary"); 
        		  header("Content-Disposition: attachment; filename=\"".$fileName."\"");
                  header("Content-length:".(string)(filesize($fileString)));
                  sleep(1);
                  fpassthru($fdl);
             }

          And when is error.inc included? Why does it output data?

            I just noticed that I didn't set my error handler. I just set it and now I dont get an error message, but I still get binary characters in the browser and the file does not download.

            Greg

              Well, don't set your error handler then. Obviously, PHP is outputting errors when left to use its own error handler - let's solve those errors first before we hide them...

              The keywords in the PHP error message is "output started at ...". What is on line 67 (and the surrounding lines) of error.inc?

                I have been using error.inc for over a year and it works. I got the script out of an oriely book called web database applications.

                Line 67 is the end of the script.

                This is the errorhandler function. Maybe I should comment out the include for error.inc and see if it downloads.

                // Abort on error. Deletes session varibles to leave
                  // us in a clean state
                  function errorHandler($errno, $errstr,
                                        $errfile, $errline)
                  {
                  $companyName = "";
                  $email = "";
                
                switch ($errno)
                {
                   case E_USER_NOTICE:
                   case E_USER_WARNING:
                   case E_WARNING:
                   case E_NOTICE:
                   case E_CORE_WARNING:
                   case E_CORE_NOTICE:
                   case E_COMPILE_WARNING:
                      break;
                   case E_USER_ERROR:
                   case E_ERROR:
                   case E_PARSE:
                   case E_CORE_ERROR:
                   case E_COMPILE_ERROR:
                      session_start();
                
                      if (session_is_registered("message"))
                         session_unregister("message");
                
                      if (session_is_registered("order_no"))
                         session_unregister("order_no");
                
                      $errorString = $companyName . " system error: $errstr (# $errno).<br>\n" .
                     "Please report the following to the administrator:<br>\n" . "Error in line $errline of
                      file $errfile.<br>\n";
                
                      // Send the error to the administrator by email
                      error_log($errorString, 1, $email);
                ?>
                
                <h2><?=$companyName;?> is temporarily unavailable</h2>
                The following has been reported to the administrator:
                <br><b><font color="red"><?=$errorString;?></b></font>
                <?php
                            // Stop the system
                            die() ;
                         default: 	
                            break;
                      }
                   }
                ?>

                  I commented out the include for the error.inc file and I get the following error message.

                  Warning: Cannot modify header information - headers already sent by (output started at /var/www/html/bestbuy/download.php:13) in /var/www/html/bestbuy/download.php on line 57

                    Okay... based on what I told you about the last message, you should know where to look for that problem. If you can't fix it, you should know what line (and surrounding code) to post...

                      I got my download script to work with a 7MB file and a 8MB memory limit. I need to be able to use it with a 13 MB file. I called the hosting company and they gave me some code to put in a .htaccess file that they said would work. I put the code in an .htaccess file and it did not work. I also noticed that the 13 MB fils has 2 dots in it like this and I read somewhere that if you have 2 dots in the file name it may not work.

                      Heres the code they gave me to put in the .htaccess file.

                      php_value memory_limit 16M

                        That's cool, but what if two people come to download an 8MB file at once? Crash.

                        So, instead of fpassthru, try something like this:

                        
                        // Protect against reaching max execution time
                        set_time_limit(0);
                        
                        //Force script termination if user aborts
                        ignore_user_abort(false);
                        
                        // Prepare headers
                        
                        
                        // Output in chunks
                        $fp= fopen ($filename 'r');
                        while (!feof ($fp) {
                        	$b= fread ($fp, 131072);  // Read in 128k chunks
                        	echo $b;
                        	// sleep(1); if you want to limit bandwidth per connection
                        }
                        fclose($fp);
                        
                        // Cleanup and/or routing
                        

                        With some header management and fseek()-ing, you can even handle download managers and partial downloads.

                          With fopen and fread can I get a file from outside the document root? I have access to the php.ini file so I can change it to even have no memory limit. I tried downloading three of the same file at the same time using fpassthru and it seemed to work.

                          I changed the php.ini file to 16MB.

                          When the user downloads the file the download script checks to see if a session is set and if the session is set then it proceeds with the download. I am having problems with sessions. When I set the session the script shows me binary characters in the browser. Do I need to destroy the session while its downloading? shouldnt session_write_close(); take care fo this for me?

                            Thanks for all your help. The script is working. I suppose I will post the finished file upload script here for the next guy who needs a working download script. You will have to change some headers if you are not downloading a zip file or if you want to allow the download of multiple zip files. I found that when using fpassthru the maximum php memory limit in php.ini pertains to a particular instance of a script running. Each time the script runs it appears that it will use up to the maximum memory limit. The configuration variables that need to be changed in the php.ini file to allow large files to be downloaded are.

                            memory_limit = 16M     ; Maximum amount of memory a script may consume (8MB)
                            ; Maximum size of POST data that PHP will accept.
                            post_max_size = 16M
                            <?php
                            // This script queries a database and downloads a zip file
                            // written by Greg Tibbetts 10/13/2006 greg@aagthosting.com
                            
                            ob_start();
                            session_start();
                            
                            // Protect against reaching max execution time 
                            set_time_limit(0);
                            
                            // Force script termination if user aborts 
                            ignore_user_abort(false);
                            
                            include '../include/db.inc';
                            include '../include/error.inc';
                            include '../include/include.inc';
                            include '../include/generalQuery.inc';
                            //include '../include/databaseUpdates.inc';
                            
                            set_error_handler("errorHandler");
                            
                            if (isset($_SESSION['targetLogin'])){
                                 // write and close the session
                            	 session_write_close();
                            
                             // Open a connection to the DBMS
                             if (!($connection = @ mysql_pconnect($hostName,
                                                             $username,
                                                             $password)))
                                  showerror();
                            
                             if (!mysql_select_db($databaseName, $connection))
                                  showerror();
                            
                             // query the download table
                             generalQuery($connection, "downloads");
                            
                             // get the results of the query from the session and unset the session		  
                             $result = $_SESSION['result'];
                             unset($_SESSION['result']);
                            
                             // fetch the row
                             $row = @ mysql_fetch_assoc($result);
                            
                             $fileDir = $row['file_path']; // supply a path name.
                             $fileName = $row['file_name']; // supply a file name.
                             $fileString = $fileDir . '/' . $fileName; // combine the path and file
                             // translate file name properly for Internet Explorer.
                             if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE")){
                                  $fileName = preg_replace('/\./', '%2e', $fileName, substr_count($fileName, '.') - 1);
                             }
                             // make sure the file exists before sending headers
                             if(!$fdl = @fopen($fileString,'r')){
                                  die("Cannot Open File!");
                             } 
                             else {
                                  //Close the session to allow for header() to be sent
                                  header('Pragma: ');// leave blank to avoid IE errors
                            	  header('Expires: 0'); 
                                  header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
                            	  header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');  
                                  header('Content-Description: File Transfer');
                                  header('Content-type: application/zip'); 
                                  header('Content-Transfer-Encoding: binary'); 
                            	  $header="Content-Disposition: attachment; filename=" . $fileName . ";"; 
                                  header($header); 
                                  header('Content-length:' . (string)(filesize($fileString)));
                                  sleep(1);
                                  fpassthru($fdl);
                            
                            	  // increment the download counter
                                  //databaseUpdates(&$connection, "downloads");
                             }
                            }
                            ob_end_flush();   
                            ?> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Video Download</title> </head> <body> </body> </html>

                            Heres showerror();

                            // The error.inc 
                            
                              // Trigger an error condition
                              function showerror(){
                                if (mysql_errno() || mysql_error())
                            	trigger_error("MySQL error: " .
                            		       mysql_errno() .
                            		       " : " . mysql_error(),
                                                   E_USER_ERROR);
                                else
                                    trigger_error("Could not connect to DBMS",
                                                   E_USER_ERROR);
                              }
                              Write a Reply...