jayarjo
jayarjo

Reputation: 16754

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.

Upvotes: 5

Views: 9346

Answers (6)

Richard Dev
Richard Dev

Reputation: 1170

Took me forever to debug. I thought it might be the proxy behind CloudFlare, Header Missing. Turned out the file I was streaming was hitting the memory limit.

I did

            ini_set('memory_limit', '128M');

Upvotes: 0

user7214700
user7214700

Reputation:

There is 1 GB limit build in Apache, even if u'll try to download a file from he root directory, completely avoiding php.

Upvotes: 0

jayarjo
jayarjo

Reputation: 16754

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.

Upvotes: 8

SteelBytes
SteelBytes

Reputation: 6965

also may need to set_time_limit(0);

Upvotes: 1

MightyE
MightyE

Reputation: 2679

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

Upvotes: 3

Layke
Layke

Reputation: 53206

Inside the php.ini you will see the setting.

I can't remember the option name off the top of my head, but I will look inside my php.ini now and try and find it.

Just remove it and it will work.

Added

Okay, someone please correct me if I am wrong, but is it

memory_limit

Upvotes: 0

Related Questions