I'm using a PHP script to control access to download files. This works fine for anything under 2Gb but fails for larger files.
Apache and PHP are both 64bit
Apache will allow the file to be downloaded if accessed directly (which I can't allow)
The guts of the PHP (ignoring the access control):
if (ob_get_level()) ob_end_clean();
error_log('FILETEST: '.$path.' : '.filesize($path));
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($path));
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($path));
readfile($path);
exit;
The error log shows the file size fine
[Tue Apr 08 11:01:16 2014] [error] [client *.*.*.*] FILETEST: /downloads/file.name : 2251373807, referer: http://myurl/files/
But the access log has a negative size:
*.*.*.* - - [08/Apr/2014:11:01:16 +0100] "GET /files/file.name HTTP/1.1" 200 -2043593489 "http://myurl/files/" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0"
And so browsers refuse to download the file. In fact, using wget, it's not sending anything:
$ wget -S -O - http://myurl/files/file.name
--2014-04-08 11:33:38-- http://myurl/files/file.name
HTTP request sent, awaiting response... No data received.
Retrying.
Try to read the file in chunks and expose them to the browser instead of filling your local memory with 2GB and flushing all at once.
Replace readfile($path); by:
#ob_end_flush();
flush();
$fileDescriptor = fopen($file, 'rb');
while ($chunk = fread($fileDescriptor, 8192)) {
echo $chunk;
#ob_end_flush();
flush();
}
fclose($fileDescriptor);
exit;
8192 bytes is a critical point in some cases, refere to php.net/fread.
Adding some microtime variables (and comparing with the pointer position of the file descriptor) will also allow you to controll the maximum speed of the download.
*(Flushing the output buffer also slightly depends on the webserver, use those commands to be sure it at least tries to flush as much as possible.)
Add code before readfile($path);
ob_clean();
flush();
I use this code for download:
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
}
Your best choice is to force apache to http chunked mode with a function like this. You'll save a lot of PHP memory this way.
function readfile_chunked($filename, $retbytes = TRUE) {
$CHUNK_SIZE=1024*1024;
$buffer = '';
$cnt =0;
$handle = fopen($filename, 'rb');
if ($handle === false) {
return false;
}
while (!feof($handle)) {
$buffer = fread($handle, $CHUNK_SIZE);
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;
}
I came across this issue before and used the below script for downloading files, it breaks the file into chunks to download large files instead of trying to take the whole file at once. This script also takes into account the browser being used as some browsers (namely IE) can handle headers slightly differently.
private function outputFile($file, $name, $mime_type='') {
$fileChunkSize = 1024*30;
if(!is_readable($file)) die('File not found or inaccessible!');
$size = filesize($file);
$name = rawurldecode($name);
$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();
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');
header("Cache-control: private");
header('Pragma: private');
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
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);
}
$chunksize = 1*($fileChunkSize);
$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);
flush();
$bytes_send += strlen($buffer);
}
fclose($file);
}
else die('Error - can not open file.');
die();
}
Related
I want to require a file to be downloaded upon the user visiting a web page with PHP. I think it has something to do with file_get_contents, but am not sure how to execute it.
$url = "http://example.com/go.exe";
After downloading a file with header(location) it is not redirecting to another page. It just stops.
Read the docs about built-in PHP function readfile
$file_url = 'http://www.myremoteserver.com/file.exe';
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . basename($file_url) . "\"");
readfile($file_url);
Also make sure to add proper content type based on your file application/zip, application/pdf etc. - but only if you do not want to trigger the save-as dialog.
<?php
$file = "http://example.com/go.exe";
header("Content-Description: File Transfer");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"". basename($file) ."\"");
readfile ($file);
exit();
?>
Or, when the file is not openable with the browser, you can just use the Location header:
<?php header("Location: http://example.com/go.exe"); ?>
header("Content-Type: application/octet-stream");
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"file.exe\"");
echo readfile($url);
is correct
or better one for exe type of files
header("Location: $url");
Display your file first and set its value into url.
index.php
<a href="download.php?download='.$row['file'].'" title="Download File">
download.php
<?php
/*db connectors*/
include('dbconfig.php');
/*function to set your files*/
function output_file($file, $name, $mime_type='')
{
if(!is_readable($file)) die('File not found or inaccessible!');
$size = filesize($file);
$name = rawurldecode($name);
$known_mime_types=array(
"htm" => "text/html",
"exe" => "application/octet-stream",
"zip" => "application/zip",
"doc" => "application/msword",
"jpg" => "image/jpg",
"php" => "text/plain",
"xls" => "application/vnd.ms-excel",
"ppt" => "application/vnd.ms-powerpoint",
"gif" => "image/gif",
"pdf" => "application/pdf",
"txt" => "text/plain",
"html"=> "text/html",
"png" => "image/png",
"jpeg"=> "image/jpg"
);
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();
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');
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);
}
$chunksize = 1*(1024*1024);
$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);
echo($buffer);
flush();
$bytes_send += strlen($buffer);
}
fclose($file);
} else
die('Error - can not open file.');
die();
}
set_time_limit(0);
/*set your folder*/
$file_path='uploads/'."your file";
/*output must be folder/yourfile*/
output_file($file_path, ''."your file".'', $row['type']);
/*back to index.php while downloading*/
header('Location:index.php');
?>
In case you have to download a file with a size larger than the allowed memory limit (memory_limit ini setting), which would cause the PHP Fatal error: Allowed memory size of 5242880 bytes exhausted error, you can do this:
// File to download.
$file = '/path/to/file';
// Maximum size of chunks (in bytes).
$maxRead = 1 * 1024 * 1024; // 1MB
// Give a nice name to your download.
$fileName = 'download_file.txt';
// Open a file in read mode.
$fh = fopen($file, 'r');
// These headers will force download on browser,
// and set the custom file name for the download, respectively.
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $fileName . '"');
// Run this until we have read the whole file.
// feof (eof means "end of file") returns `true` when the handler
// has reached the end of file.
while (!feof($fh)) {
// Read and output the next chunk.
echo fread($fh, $maxRead);
// Flush the output buffer to free memory.
ob_flush();
}
// Exit to make sure not to output anything else.
exit;
A modification of the accepted answer above, which also detects the MIME type in runtime:
$finfo = finfo_open(FILEINFO_MIME_TYPE);
header('Content-Type: '.finfo_file($finfo, $path));
$finfo = finfo_open(FILEINFO_MIME_ENCODING);
header('Content-Transfer-Encoding: '.finfo_file($finfo, $path));
header('Content-disposition: attachment; filename="'.basename($path).'"');
readfile($path); // do the double-download-dance (dirty but worky)
The answers above me works. But, I'd like to contribute a method on how to perform it using GET
on your html/php page
$File = 'some/dir/file.jpg';
Download
and download.php contains
$file = $_GET['f'];
header("Expires: 0");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
$ext = pathinfo($file, PATHINFO_EXTENSION);
$basename = pathinfo($file, PATHINFO_BASENAME);
header("Content-type: application/".$ext);
header('Content-length: '.filesize($file));
header("Content-Disposition: attachment; filename=\"$basename\"");
ob_clean();
flush();
readfile($file);
exit;
this should work on any file types. this is not tested using POST, but it could work.
you can use download attribute to force download a file:
<a href="https://test.com/aaa.exe" download>click here to download</a>
You can stream download too which will consume significantly less resource.
example:
$readableStream = fopen('test.zip', 'rb');
$writableStream = fopen('php://output', 'wb');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="test.zip"');
stream_copy_to_stream($readableStream, $writableStream);
ob_flush();
flush();
In the above example, I am downloading a test.zip (which was actually the android studio zip on my local machine).
php://output is a write-only stream (generally used by echo or print).
after that, you just need to set the required headers and call stream_copy_to_stream(source, destination).
stream_copy_to_stream() method acts as a pipe which takes the input from the source stream (read stream) and pipes it to the destination stream (write stream) and it also avoids the issue of allowed memory exhausted so you can actually download files that are bigger than your PHP memory_limit.
The following code is a correct way of implementing a download service in php as explained in the following tutorial
header('Content-Type: application/zip');
header("Content-Disposition: attachment; filename=\"$file_name\"");
set_time_limit(0);
$file = #fopen($filePath, "rb");
while(!feof($file)) {
print(#fread($file, 1024*8));
ob_flush();
flush();
}
try this:
header('Content-type: audio/mp3');
header('Content-disposition: attachment;
filename=“'.$trackname'”');
readfile('folder name /'.$trackname);
exit();
http://php.net/manual/en/function.readfile.php
That's all you need. "Monkey.gif" change to your file name. If you need to download from other server, "monkey.gif" change to "http://www.exsample.com/go.exe"
I want to require a file to be downloaded upon the user visiting a web page with PHP. I think it has something to do with file_get_contents, but am not sure how to execute it.
$url = "http://example.com/go.exe";
After downloading a file with header(location) it is not redirecting to another page. It just stops.
Read the docs about built-in PHP function readfile
$file_url = 'http://www.myremoteserver.com/file.exe';
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . basename($file_url) . "\"");
readfile($file_url);
Also make sure to add proper content type based on your file application/zip, application/pdf etc. - but only if you do not want to trigger the save-as dialog.
<?php
$file = "http://example.com/go.exe";
header("Content-Description: File Transfer");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"". basename($file) ."\"");
readfile ($file);
exit();
?>
Or, when the file is not openable with the browser, you can just use the Location header:
<?php header("Location: http://example.com/go.exe"); ?>
header("Content-Type: application/octet-stream");
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"file.exe\"");
echo readfile($url);
is correct
or better one for exe type of files
header("Location: $url");
Display your file first and set its value into url.
index.php
<a href="download.php?download='.$row['file'].'" title="Download File">
download.php
<?php
/*db connectors*/
include('dbconfig.php');
/*function to set your files*/
function output_file($file, $name, $mime_type='')
{
if(!is_readable($file)) die('File not found or inaccessible!');
$size = filesize($file);
$name = rawurldecode($name);
$known_mime_types=array(
"htm" => "text/html",
"exe" => "application/octet-stream",
"zip" => "application/zip",
"doc" => "application/msword",
"jpg" => "image/jpg",
"php" => "text/plain",
"xls" => "application/vnd.ms-excel",
"ppt" => "application/vnd.ms-powerpoint",
"gif" => "image/gif",
"pdf" => "application/pdf",
"txt" => "text/plain",
"html"=> "text/html",
"png" => "image/png",
"jpeg"=> "image/jpg"
);
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();
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');
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);
}
$chunksize = 1*(1024*1024);
$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);
echo($buffer);
flush();
$bytes_send += strlen($buffer);
}
fclose($file);
} else
die('Error - can not open file.');
die();
}
set_time_limit(0);
/*set your folder*/
$file_path='uploads/'."your file";
/*output must be folder/yourfile*/
output_file($file_path, ''."your file".'', $row['type']);
/*back to index.php while downloading*/
header('Location:index.php');
?>
In case you have to download a file with a size larger than the allowed memory limit (memory_limit ini setting), which would cause the PHP Fatal error: Allowed memory size of 5242880 bytes exhausted error, you can do this:
// File to download.
$file = '/path/to/file';
// Maximum size of chunks (in bytes).
$maxRead = 1 * 1024 * 1024; // 1MB
// Give a nice name to your download.
$fileName = 'download_file.txt';
// Open a file in read mode.
$fh = fopen($file, 'r');
// These headers will force download on browser,
// and set the custom file name for the download, respectively.
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $fileName . '"');
// Run this until we have read the whole file.
// feof (eof means "end of file") returns `true` when the handler
// has reached the end of file.
while (!feof($fh)) {
// Read and output the next chunk.
echo fread($fh, $maxRead);
// Flush the output buffer to free memory.
ob_flush();
}
// Exit to make sure not to output anything else.
exit;
A modification of the accepted answer above, which also detects the MIME type in runtime:
$finfo = finfo_open(FILEINFO_MIME_TYPE);
header('Content-Type: '.finfo_file($finfo, $path));
$finfo = finfo_open(FILEINFO_MIME_ENCODING);
header('Content-Transfer-Encoding: '.finfo_file($finfo, $path));
header('Content-disposition: attachment; filename="'.basename($path).'"');
readfile($path); // do the double-download-dance (dirty but worky)
The answers above me works. But, I'd like to contribute a method on how to perform it using GET
on your html/php page
$File = 'some/dir/file.jpg';
Download
and download.php contains
$file = $_GET['f'];
header("Expires: 0");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
$ext = pathinfo($file, PATHINFO_EXTENSION);
$basename = pathinfo($file, PATHINFO_BASENAME);
header("Content-type: application/".$ext);
header('Content-length: '.filesize($file));
header("Content-Disposition: attachment; filename=\"$basename\"");
ob_clean();
flush();
readfile($file);
exit;
this should work on any file types. this is not tested using POST, but it could work.
you can use download attribute to force download a file:
<a href="https://test.com/aaa.exe" download>click here to download</a>
You can stream download too which will consume significantly less resource.
example:
$readableStream = fopen('test.zip', 'rb');
$writableStream = fopen('php://output', 'wb');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="test.zip"');
stream_copy_to_stream($readableStream, $writableStream);
ob_flush();
flush();
In the above example, I am downloading a test.zip (which was actually the android studio zip on my local machine).
php://output is a write-only stream (generally used by echo or print).
after that, you just need to set the required headers and call stream_copy_to_stream(source, destination).
stream_copy_to_stream() method acts as a pipe which takes the input from the source stream (read stream) and pipes it to the destination stream (write stream) and it also avoids the issue of allowed memory exhausted so you can actually download files that are bigger than your PHP memory_limit.
The following code is a correct way of implementing a download service in php as explained in the following tutorial
header('Content-Type: application/zip');
header("Content-Disposition: attachment; filename=\"$file_name\"");
set_time_limit(0);
$file = #fopen($filePath, "rb");
while(!feof($file)) {
print(#fread($file, 1024*8));
ob_flush();
flush();
}
try this:
header('Content-type: audio/mp3');
header('Content-disposition: attachment;
filename=“'.$trackname'”');
readfile('folder name /'.$trackname);
exit();
http://php.net/manual/en/function.readfile.php
That's all you need. "Monkey.gif" change to your file name. If you need to download from other server, "monkey.gif" change to "http://www.exsample.com/go.exe"
I want to require a file to be downloaded upon the user visiting a web page with PHP. I think it has something to do with file_get_contents, but am not sure how to execute it.
$url = "http://example.com/go.exe";
After downloading a file with header(location) it is not redirecting to another page. It just stops.
Read the docs about built-in PHP function readfile
$file_url = 'http://www.myremoteserver.com/file.exe';
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . basename($file_url) . "\"");
readfile($file_url);
Also make sure to add proper content type based on your file application/zip, application/pdf etc. - but only if you do not want to trigger the save-as dialog.
<?php
$file = "http://example.com/go.exe";
header("Content-Description: File Transfer");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"". basename($file) ."\"");
readfile ($file);
exit();
?>
Or, when the file is not openable with the browser, you can just use the Location header:
<?php header("Location: http://example.com/go.exe"); ?>
header("Content-Type: application/octet-stream");
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"file.exe\"");
echo readfile($url);
is correct
or better one for exe type of files
header("Location: $url");
Display your file first and set its value into url.
index.php
<a href="download.php?download='.$row['file'].'" title="Download File">
download.php
<?php
/*db connectors*/
include('dbconfig.php');
/*function to set your files*/
function output_file($file, $name, $mime_type='')
{
if(!is_readable($file)) die('File not found or inaccessible!');
$size = filesize($file);
$name = rawurldecode($name);
$known_mime_types=array(
"htm" => "text/html",
"exe" => "application/octet-stream",
"zip" => "application/zip",
"doc" => "application/msword",
"jpg" => "image/jpg",
"php" => "text/plain",
"xls" => "application/vnd.ms-excel",
"ppt" => "application/vnd.ms-powerpoint",
"gif" => "image/gif",
"pdf" => "application/pdf",
"txt" => "text/plain",
"html"=> "text/html",
"png" => "image/png",
"jpeg"=> "image/jpg"
);
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();
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');
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);
}
$chunksize = 1*(1024*1024);
$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);
echo($buffer);
flush();
$bytes_send += strlen($buffer);
}
fclose($file);
} else
die('Error - can not open file.');
die();
}
set_time_limit(0);
/*set your folder*/
$file_path='uploads/'."your file";
/*output must be folder/yourfile*/
output_file($file_path, ''."your file".'', $row['type']);
/*back to index.php while downloading*/
header('Location:index.php');
?>
In case you have to download a file with a size larger than the allowed memory limit (memory_limit ini setting), which would cause the PHP Fatal error: Allowed memory size of 5242880 bytes exhausted error, you can do this:
// File to download.
$file = '/path/to/file';
// Maximum size of chunks (in bytes).
$maxRead = 1 * 1024 * 1024; // 1MB
// Give a nice name to your download.
$fileName = 'download_file.txt';
// Open a file in read mode.
$fh = fopen($file, 'r');
// These headers will force download on browser,
// and set the custom file name for the download, respectively.
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $fileName . '"');
// Run this until we have read the whole file.
// feof (eof means "end of file") returns `true` when the handler
// has reached the end of file.
while (!feof($fh)) {
// Read and output the next chunk.
echo fread($fh, $maxRead);
// Flush the output buffer to free memory.
ob_flush();
}
// Exit to make sure not to output anything else.
exit;
A modification of the accepted answer above, which also detects the MIME type in runtime:
$finfo = finfo_open(FILEINFO_MIME_TYPE);
header('Content-Type: '.finfo_file($finfo, $path));
$finfo = finfo_open(FILEINFO_MIME_ENCODING);
header('Content-Transfer-Encoding: '.finfo_file($finfo, $path));
header('Content-disposition: attachment; filename="'.basename($path).'"');
readfile($path); // do the double-download-dance (dirty but worky)
The answers above me works. But, I'd like to contribute a method on how to perform it using GET
on your html/php page
$File = 'some/dir/file.jpg';
Download
and download.php contains
$file = $_GET['f'];
header("Expires: 0");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
$ext = pathinfo($file, PATHINFO_EXTENSION);
$basename = pathinfo($file, PATHINFO_BASENAME);
header("Content-type: application/".$ext);
header('Content-length: '.filesize($file));
header("Content-Disposition: attachment; filename=\"$basename\"");
ob_clean();
flush();
readfile($file);
exit;
this should work on any file types. this is not tested using POST, but it could work.
you can use download attribute to force download a file:
<a href="https://test.com/aaa.exe" download>click here to download</a>
You can stream download too which will consume significantly less resource.
example:
$readableStream = fopen('test.zip', 'rb');
$writableStream = fopen('php://output', 'wb');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="test.zip"');
stream_copy_to_stream($readableStream, $writableStream);
ob_flush();
flush();
In the above example, I am downloading a test.zip (which was actually the android studio zip on my local machine).
php://output is a write-only stream (generally used by echo or print).
after that, you just need to set the required headers and call stream_copy_to_stream(source, destination).
stream_copy_to_stream() method acts as a pipe which takes the input from the source stream (read stream) and pipes it to the destination stream (write stream) and it also avoids the issue of allowed memory exhausted so you can actually download files that are bigger than your PHP memory_limit.
The following code is a correct way of implementing a download service in php as explained in the following tutorial
header('Content-Type: application/zip');
header("Content-Disposition: attachment; filename=\"$file_name\"");
set_time_limit(0);
$file = #fopen($filePath, "rb");
while(!feof($file)) {
print(#fread($file, 1024*8));
ob_flush();
flush();
}
try this:
header('Content-type: audio/mp3');
header('Content-disposition: attachment;
filename=“'.$trackname'”');
readfile('folder name /'.$trackname);
exit();
http://php.net/manual/en/function.readfile.php
That's all you need. "Monkey.gif" change to your file name. If you need to download from other server, "monkey.gif" change to "http://www.exsample.com/go.exe"
I have a resumable download php script.
It works fine on Apache server but not on IIS 7 ( which my client currently use)
The problem with IIS is:
When downloading file,other page at same site will freeze.
(even display 500 server error sometimes)
(same script on a Apache server does not carry the same problem)
The problem disappared if I turnoff resumable support
(even downloading on a download manager will freeze all browsers viewing the same site)
This make me believe IIS need some configuration? or php.ini?
I have no luck with google so far any help will be grateful
and.. yes, I have access on IIS and php.ini
and yes..I setup the maximum connection time on IIS already(needed for large file transfer)
this script is..
(anybody come across here and like to use this script for large file tranfer on IIS please read -->php on IIS 7 >> FastCGI timeout settings<< )
$filename="test.flv";
$filepath="zekkai.flv";
//set mime
$mime_type="";
$known_mime_types=array(
"flv" => "video/x-flv",
"mp4" => "video/mp4",
"mov" => "video/quicktime",
"avi" => "video/x-msvideo",
"wmv" => " video/x-ms-wmv "
);
if($mime_type==''){
$file_extension = strtolower(substr(strrchr($filepath,"."),1));
if(array_key_exists($file_extension, $known_mime_types)){
$mime_type=$known_mime_types[$file_extension];
} else {
$mime_type="application/force-download";
};
};
header("Connection: Keep-Alive");
header("Keep-Alive: timeout=65000");
$fsize=filesize($filepath);
set_time_limit(0);
//turn off buffer
ob_end_clean();
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');
header("Content-Description: File Transfer");
header("Content-type: ".$mime_type);
header("Content-Disposition: attachment; filename=\"".$filename."\"");
header("Content-Transfer-Encoding: binary");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header('Accept-Ranges: bytes');
header("Cache-control: public");
header('Pragma: public');
header("Expires: 0");
// resumable support..
if(isset($_SERVER['HTTP_RANGE'])){
// delete this part to turnoff resumable support
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=$fsize-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/$fsize");
// resumable support end
} else {
$new_length=$fsize;
header("Content-Length: ".$fsize);
}
/* output the file itself */
$chunksize = 3*(1024*1024); //you may want to change this
$bytes_send = 0;
if ($Source_File = fopen($filepath, 'rb')){
if(isset($_SERVER['HTTP_RANGE'])){
fseek($Source_File, $range);
}
while(!feof($Source_File) && (!connection_aborted()) && ($bytes_send<$new_length) ) {
$buffer = fread($Source_File, $chunksize);
print($buffer); //echo($buffer); // is also possible
flush();
$bytes_send += strlen($buffer);
}
fclose($Source_File);
} else die('Error - can not open file.');
exit();
note.
none php script is not affected.
only php page affect by the download..
so my guess the problem is realted to fastcgi module?
I want to require a file to be downloaded upon the user visiting a web page with PHP. I think it has something to do with file_get_contents, but am not sure how to execute it.
$url = "http://example.com/go.exe";
After downloading a file with header(location) it is not redirecting to another page. It just stops.
Read the docs about built-in PHP function readfile
$file_url = 'http://www.myremoteserver.com/file.exe';
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . basename($file_url) . "\"");
readfile($file_url);
Also make sure to add proper content type based on your file application/zip, application/pdf etc. - but only if you do not want to trigger the save-as dialog.
<?php
$file = "http://example.com/go.exe";
header("Content-Description: File Transfer");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"". basename($file) ."\"");
readfile ($file);
exit();
?>
Or, when the file is not openable with the browser, you can just use the Location header:
<?php header("Location: http://example.com/go.exe"); ?>
header("Content-Type: application/octet-stream");
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"file.exe\"");
echo readfile($url);
is correct
or better one for exe type of files
header("Location: $url");
Display your file first and set its value into url.
index.php
<a href="download.php?download='.$row['file'].'" title="Download File">
download.php
<?php
/*db connectors*/
include('dbconfig.php');
/*function to set your files*/
function output_file($file, $name, $mime_type='')
{
if(!is_readable($file)) die('File not found or inaccessible!');
$size = filesize($file);
$name = rawurldecode($name);
$known_mime_types=array(
"htm" => "text/html",
"exe" => "application/octet-stream",
"zip" => "application/zip",
"doc" => "application/msword",
"jpg" => "image/jpg",
"php" => "text/plain",
"xls" => "application/vnd.ms-excel",
"ppt" => "application/vnd.ms-powerpoint",
"gif" => "image/gif",
"pdf" => "application/pdf",
"txt" => "text/plain",
"html"=> "text/html",
"png" => "image/png",
"jpeg"=> "image/jpg"
);
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();
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');
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);
}
$chunksize = 1*(1024*1024);
$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);
echo($buffer);
flush();
$bytes_send += strlen($buffer);
}
fclose($file);
} else
die('Error - can not open file.');
die();
}
set_time_limit(0);
/*set your folder*/
$file_path='uploads/'."your file";
/*output must be folder/yourfile*/
output_file($file_path, ''."your file".'', $row['type']);
/*back to index.php while downloading*/
header('Location:index.php');
?>
In case you have to download a file with a size larger than the allowed memory limit (memory_limit ini setting), which would cause the PHP Fatal error: Allowed memory size of 5242880 bytes exhausted error, you can do this:
// File to download.
$file = '/path/to/file';
// Maximum size of chunks (in bytes).
$maxRead = 1 * 1024 * 1024; // 1MB
// Give a nice name to your download.
$fileName = 'download_file.txt';
// Open a file in read mode.
$fh = fopen($file, 'r');
// These headers will force download on browser,
// and set the custom file name for the download, respectively.
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $fileName . '"');
// Run this until we have read the whole file.
// feof (eof means "end of file") returns `true` when the handler
// has reached the end of file.
while (!feof($fh)) {
// Read and output the next chunk.
echo fread($fh, $maxRead);
// Flush the output buffer to free memory.
ob_flush();
}
// Exit to make sure not to output anything else.
exit;
A modification of the accepted answer above, which also detects the MIME type in runtime:
$finfo = finfo_open(FILEINFO_MIME_TYPE);
header('Content-Type: '.finfo_file($finfo, $path));
$finfo = finfo_open(FILEINFO_MIME_ENCODING);
header('Content-Transfer-Encoding: '.finfo_file($finfo, $path));
header('Content-disposition: attachment; filename="'.basename($path).'"');
readfile($path); // do the double-download-dance (dirty but worky)
The answers above me works. But, I'd like to contribute a method on how to perform it using GET
on your html/php page
$File = 'some/dir/file.jpg';
Download
and download.php contains
$file = $_GET['f'];
header("Expires: 0");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
$ext = pathinfo($file, PATHINFO_EXTENSION);
$basename = pathinfo($file, PATHINFO_BASENAME);
header("Content-type: application/".$ext);
header('Content-length: '.filesize($file));
header("Content-Disposition: attachment; filename=\"$basename\"");
ob_clean();
flush();
readfile($file);
exit;
this should work on any file types. this is not tested using POST, but it could work.
you can use download attribute to force download a file:
<a href="https://test.com/aaa.exe" download>click here to download</a>
You can stream download too which will consume significantly less resource.
example:
$readableStream = fopen('test.zip', 'rb');
$writableStream = fopen('php://output', 'wb');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="test.zip"');
stream_copy_to_stream($readableStream, $writableStream);
ob_flush();
flush();
In the above example, I am downloading a test.zip (which was actually the android studio zip on my local machine).
php://output is a write-only stream (generally used by echo or print).
after that, you just need to set the required headers and call stream_copy_to_stream(source, destination).
stream_copy_to_stream() method acts as a pipe which takes the input from the source stream (read stream) and pipes it to the destination stream (write stream) and it also avoids the issue of allowed memory exhausted so you can actually download files that are bigger than your PHP memory_limit.
The following code is a correct way of implementing a download service in php as explained in the following tutorial
header('Content-Type: application/zip');
header("Content-Disposition: attachment; filename=\"$file_name\"");
set_time_limit(0);
$file = #fopen($filePath, "rb");
while(!feof($file)) {
print(#fread($file, 1024*8));
ob_flush();
flush();
}
try this:
header('Content-type: audio/mp3');
header('Content-disposition: attachment;
filename=“'.$trackname'”');
readfile('folder name /'.$trackname);
exit();
http://php.net/manual/en/function.readfile.php
That's all you need. "Monkey.gif" change to your file name. If you need to download from other server, "monkey.gif" change to "http://www.exsample.com/go.exe"