Unable to view the download PDF file in browser in php - php

I have a download media function in which all type of media files gets downloaded. Below is the code
public function downloadMedia($file, $filename_direct = '', $extern = '', $exitHere = 1)
{
jimport('joomla.filesystem.file');
clearstatcache();
if (!$extern)
{
if (!JFile::exists($file))
{
return 2;
}
else
{
$len = filesize($file);
}
}
else
{
/* Return the size of a remote url or a local file specified by $url.
$thereturn specifies the unit returned (either bytes "", MiB "mb" or KiB
"kb"). */
$len = filesize($file);
}
$filename = basename($file);
$file_extension = strtolower(substr(strrchr($filename, "."), 1));
$ctype = $this->getMime($file_extension);
ob_end_clean();
// Needed for MS IE - otherwise content disposition is not used?
if (ini_get('zlib.output_compression'))
{
ini_set('zlib.output_compression', 'Off');
}
header("Cache-Control: public, must-revalidate");
header('Cache-Control: pre-check=0, post-check=0, max-age=0');
header("Expires: 0");
header("Content-Description: File Transfer");
header("Content-Type: " . $ctype);
header("Content-Length: " . (string) $len);
header('Content-Disposition: attachment; filename="' . $filename . '"');
// set_time_limit doesn't work in safe mode
if (!ini_get('safe_mode'))
{
#set_time_limit(0);
}
/*#readfile($file);
if ($exitHere == 1)
{
exit;
}*/
$fp = fopen($file, "r") ;
ob_clean();
flush();
while (!#feof($fp)) {
$buff = #fread($fp, $len);
print $buff;
}
exit;
}
Now the issue is, whenever I download the PDF file & clicked on downloaded file it shows an error in a browser like 'Failed to load PDF document.'
The downloaded PDF file is opened correctly only when if I open the file in adobe instead of opening it in browser.

Related

readfile returns a corrupt zip download

I have the following code in my Phalcon application.
public function downloadAction($key = NULL) {
if ($key == NULL) {
return $this->respondDownloadFailed();
}
$url = Urls::findFirst("token = '".$key."'");
if ($url) {
$path = $url->getUrl();
$ext = pathinfo($path, PATHINFO_EXTENSION);
header("Content-type: application/".$ext);
header("Content-Disposition: attachment; filename=".$key.".".$ext);
header("Content-length: " . filesize($path));
header("Pragma: no-cache");
header("Expires: 0");
ob_clean();
flush();
readfile($path);
unlink($path);
die;
}
return $this->respondDownloadFailed();
}
Now, when the .zip is created on the server I can open and unzip it. However, when I download it through the code above, I get a zip file which then unarchives to a .cpgz file. When unarchiving that one, I get a .zip again. Any ideas as to what is going wrong?

Download file via PHP script from FTP server to browser with Content-Length header without storing the file on the web server

I use this code to download a file to memory from ftp:
public static function getFtpFileContents($conn_id , $file)
{
ob_start();
$result = ftp_get($conn_id, "php://output", $file, FTP_BINARY);
$data = ob_get_contents();
ob_end_clean();
if ($resul)
return $data;
return null;
}
How can I make it directly send the file to the user (browser) without saving to disk and without redirecting to the ftp server ?
Just remove the output buffering (ob_start() and the others).
Use just this:
ftp_get($conn_id, "php://output", $file, FTP_BINARY);
Though if you want to add Content-Length header, you have to query file size first using ftp_size:
$conn_id = ftp_connect("ftp.example.com");
ftp_login($conn_id, "username", "password");
ftp_pasv($conn_id, true);
$file_path = "remote/path/file.zip";
$size = ftp_size($conn_id, $file_path);
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=" . basename($file_path));
header("Content-Length: $size");
ftp_get($conn_id, "php://output", $file_path, FTP_BINARY);
(add error handling)
For more broad background, see:
List and download clicked file from FTP
public static function getFtpFileContentsWithSize($conn_id , $file)
{
ob_start();
$result = ftp_get($conn_id, "php://output", $file, FTP_BINARY);
$data = ob_get_contents();
$datasize = ob_get_length( );
ob_end_clean();
if ($result)
return array( 'data' => $data, 'size' => $datasize );
return null;
}
$mapfile = SUPERFTP::getFtpFileContentsWithSize($ftpconn, $curmap['filename']);
ftp_close($ftpconn);
if (!$mapfile)
{
$viewParams['OutContext'] = "Error. File not found." ;
}
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.$curmap['filename']);
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: ' . $mapfile['size']);
echo $mapfile['data'];
exit( );
This code works. Thanks all.

PHP readfile() displays the file contents instead of forcing the browser to download the file [duplicate]

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"

PHP displays the content of file instead of downloading it [duplicate]

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"

Download of large files does not work

Hey i have following code to download a large file but the download does stop everytime without finish the download
function download($file)
{
include('logger.php5');
$log = new Logging();
$log->lfile('download.log');
ini_set('max_execution_time', 86400);
//header('Location: '.$file);
$filesize = filesize($file);
$filename = pathinfo($file, PATHINFO_BASENAME);
$filext = pathinfo($file, PATHINFO_EXTENSION);
$mime = include('mime.php5');
$log->lwrite(ini_get('max_execution_time'));
$log->lwrite(sprintf('%s %s %s %s', $filename, $filext, $mime[$filext], human_filesize($filesize)));
$log->lclose();
#ob_end_clean();
session_write_close();
header("Content-Description: File Transfer");
header("Content-Type: ".$mime[$filext]);
header("Content-Disposition: ".
(!strpos($HTTP_USER_AGENT,"MSIE 5.5")?"attachment; ":"").
"filename=".$filename);
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".$filesize);
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header('Pragma: public');
header('Expires: 0');
$done = readfile_chunked($file);
}
function readfile_chunked($filename,$retbytes=true) {
$chunksize = 1*(1024*1024); // how many bytes per chunk
$buffer = '';
$cnt =0;
// $handle = fopen($filename, 'rb');
$handle = fopen($filename, 'rb');
if ($handle === false) {
return false;
}
while (!feof($handle)) {
$buffer = fread($handle, $chunksize);
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;
}
Each time i call the script the download start up but stops after 400MB, the file itself is 778MB big.
Someone can see a problem with the code?
UPDATE
after try to log the return value of readfile_chunkedit feels like the script gets stoped not the download itself. Because i cant get a log entry after the readfile_chunked call.
It could be a problem with the filesize function in PHP. There are known bugs for big file size reading and as you're sending it with the file as an header I would suggest you to try the script without using this line:
header("Content-Length: ".$filesize);
Oh and maybe you can take a look at this line:
header("Content-Transfer-Encoding: binary");
I think the encoding should be checked for each file. Like this:
$finfo = finfo_open(FILEINFO_MIME);
//check to see if the mime-type starts with 'text'
return substr(finfo_file($finfo, $filename), 0, 4) == 'text';
If it's a textfile you should use ASCII ofcourse. Has nothing to do with the question but I think it's an useful addition to your script :)

Categories