Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there some limit on a size of a file when causing a download with PHP?

Trying to force-download file with PHP using usual:

header("Content-type: $type" );
header("Content-Disposition: attachment; filename=$name");
header('Content-Length: ' . filesize($path));

And it does successfully for files somewhere below 32 mb. For bigger ones it just returns zeroed file.

Obviously there's some kind of limit, but what sets it? Using Apache 2.2.11 and PHP 5.3.0.

like image 703
jayarjo Avatar asked Feb 23 '10 17:02

jayarjo


3 Answers

I eventually stumbled on this post: http://w-shadow.com/blog/2007/08/12/how-to-force-file-download-with-php/.

function output_file($file, $name, $mime_type='')
{
 /*
 This function takes a path to a file to output ($file), 
 the filename that the browser will see ($name) and 
 the MIME type of the file ($mime_type, optional).

 If you want to do something on download abort/finish,
 register_shutdown_function('function_name');
 */
 if(!is_readable($file)) die('File not found or inaccessible!');

 $size = filesize($file);
 $name = rawurldecode($name);

 /* Figure out the MIME type (if not specified) */
 $known_mime_types=array(
    "pdf" => "application/pdf",
    "txt" => "text/plain",
    "html" => "text/html",
    "htm" => "text/html",
    "exe" => "application/octet-stream",
    "zip" => "application/zip",
    "doc" => "application/msword",
    "xls" => "application/vnd.ms-excel",
    "ppt" => "application/vnd.ms-powerpoint",
    "gif" => "image/gif",
    "png" => "image/png",
    "jpeg"=> "image/jpg",
    "jpg" =>  "image/jpg",
    "php" => "text/plain"
 );

 if($mime_type==''){
     $file_extension = strtolower(substr(strrchr($file,"."),1));
     if(array_key_exists($file_extension, $known_mime_types)){
        $mime_type=$known_mime_types[$file_extension];
     } else {
        $mime_type="application/force-download";
     };
 };

 ob_end_clean(); //turn off output buffering to decrease cpu usage

 // required for IE, otherwise Content-Disposition may be ignored
 if(ini_get('zlib.output_compression'))
  ini_set('zlib.output_compression', 'Off');

 header('Content-Type: ' . $mime_type);
 header('Content-Disposition: attachment; filename="'.$name.'"');
 header("Content-Transfer-Encoding: binary");
 header('Accept-Ranges: bytes');

 /* The three lines below basically make the 
    download non-cacheable */
 header("Cache-control: private");
 header('Pragma: private');
 header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");

 // multipart-download and download resuming support
 if(isset($_SERVER['HTTP_RANGE']))
 {
    list($a, $range) = explode("=",$_SERVER['HTTP_RANGE'],2);
    list($range) = explode(",",$range,2);
    list($range, $range_end) = explode("-", $range);
    $range=intval($range);
    if(!$range_end) {
        $range_end=$size-1;
    } else {
        $range_end=intval($range_end);
    }

    $new_length = $range_end-$range+1;
    header("HTTP/1.1 206 Partial Content");
    header("Content-Length: $new_length");
    header("Content-Range: bytes $range-$range_end/$size");
 } else {
    $new_length=$size;
    header("Content-Length: ".$size);
 }

 /* output the file itself */
 $chunksize = 1*(1024*1024); //you may want to change this
 $bytes_send = 0;
 if ($file = fopen($file, 'r'))
 {
    if(isset($_SERVER['HTTP_RANGE']))
    fseek($file, $range);

    while(!feof($file) && 
        (!connection_aborted()) && 
        ($bytes_send<$new_length)
          )
    {
        $buffer = fread($file, $chunksize);
        print($buffer); //echo($buffer); // is also possible
        flush();
        $bytes_send += strlen($buffer);
    }
 fclose($file);
 } else die('Error - can not open file.');

die();
}   

/*********************************************
            Example of use
**********************************************/

/* 
Make sure script execution doesn't time out.
Set maximum execution time in seconds (0 means no limit).
*/
set_time_limit(0);  
$file_path='that_one_file.txt';
output_file($file_path, 'some file.txt', 'text/plain');

Adding all the headers recommended there and also using:

 ob_end_clean(); //turn off output buffering to decrease cpu usage

before any output - has helped. No more limitations observable. Files download completely even huge ones.

like image 158
jayarjo Avatar answered Nov 11 '22 18:11

jayarjo


It seems like you're loading the entire file into RAM before sending it down to the recipient. You'll want to look into PHP Streams to be able to send the full file contents without having to read it all into RAM first: http://php.net/streams

like image 21
MightyE Avatar answered Nov 11 '22 19:11

MightyE


also may need to set_time_limit(0);

like image 24
SteelBytes Avatar answered Nov 11 '22 17:11

SteelBytes