PHP forcing browser to download files (two ways) - php

I want a php script to download files of any type without opening it. I have found the following functions, which seem to me a bit different, but I don`t know which is better. Please let me know which one is better and reliable:
From PHP Manual
$file = 'monkey.gif';
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');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
}
From other tutorial:
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=\"{$file->filename}\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . $filesize);
// download
// #readfile($file_path);
$dwn_file = #fopen($file_path,"rb");
if ($dwn_file) {
while(!feof($dwn_file)) {
print(fread($dwn_file, 1024*8));
flush();
if (connection_status()!=0) {
#fclose($dwn_file);
die();
}
}
#fclose($dwn_file);
}

Both are actually pretty similar when you look at the headers that are being sent, which is what forces the download. The difference is in how the file is read. The first one uses readfile() as an abstraction while the second one reads byte per byte.
The second example also uses the # symbol several times to suppress errors which may not be a good thing to copy.
I'd use the code from the PHP manual. It's simpler, less error-prone, and more readable.

They are both basically the same. What you want is the headers
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=\"{$file->filename}\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . $filesize);
header('Content-Type: image/gif');
And then simply output the file. This can be done in numerous ways, but I would recommend simply:
readfile($filename);
as it is self-explanatory. It will read the file and output it to the output buffer (i.e. the browser).
Note however that the Content-Type header should be set to image/gif if that is what you are outputting.

This is an opinion, but I would use the first one, it's a lot quicker and cleaner.

Related

PHP File Download Issue - Can't Read

I have the following code for downloading files automatically
at the click of a submit button, everything seems to work fine;
the file downloads in the rigth format, right size, right name, but when I
want to open it, I get an error, the file cannot be read, what could be the
problem?
$file=mysql_fetch_assoc($sel);
$file=$file['downloadlink'];
header('Content-Type: "application/octet-stream"');
header("Content-Transfer-Encoding: Binary");
header("Content-length: ".filesize($file));
header("Content-disposition: attachment; filename=\"".basename($file)."\"");
readfile($file);
you could try tweaking this function from the readfile() comments:
function DownloadFile($file) { // $file = include path
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;
}
}
I have an addition for this. If the file size is very big, it'll download empty file which we cannot open at all. That is not a problem with 'readfile' function itself. The problem is reading large files into memory. So, for preventing that kind of issues, we have to use 'ob_end_flush()' immediately before to the 'readfile' function for turning off output buffer.
Hope this tip will save someones time. :)

Download Excel From Server Using PHP

Can anyone help me to write code on PHP to download Excel File from the server.
I have created below codes using header and readfile but the downloaded file was corrupted.
//content type
header('Content-type: application/vnd.ms-excel');
//open/save dialog box
header('Content-Disposition: attachment; filename='.$fileName);
//read from server and write to buffer
readfile($reportPath);
Can anyone help me on the best way to download existing Excel file from the server.
Please see below image of data after downloaded
ob_clean();
put that code before all header declaration
I suggest to use the X-SENDFILE header. https://tn123.org/mod_xsendfile/
I use something like the following:
header("Content-Description: File Transfer");
header("Content-Type: application/octet-stream");
header('Content-Disposition: attachment; filename="'.basename($path).'"');
header("Content-Transfer-Encoding: binary");
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header("Content-Type: application/force-download");
header("Content-Type: application/download");
header("Content-Length: ".filesize($path));
readfile($path);
exit;
Make sure you're not outputting anything before or after the readfile.

PHP Download Script Creates Unreadable ZIP File on Mac

For reference, I have already read and tried the answers in these and several other threads:
Creating and serving zipped files with php
Opening downloaded zip file creates cpgz file?
I have a zip file on my server.
When I use Filezilla to move that Zip file from my server to my Mac, I can open it normally.
When I use this PHP code to download the Zip file to my Linux machine, it opens normally.
When I use this PHP code to download the Zip file to my Mac, using Safari or Firefox, I get an error saying "Decompression Failed" or "The structure of the archive is damaged" or I get a .cpgz file - which I believe means that the computer is zipping the file, not unzipping it.
Here is the PHP code I am using to deliver the zip file.
$zipname = "myfile.zip";
$zippath = "/path/to/" . $zipname;
if ($downloadzip = fopen ($zippath, "r")) {
$fsize = filesize($zippath);
header("Content-type: application/zip");
header("Content-Disposition: attachment; filename=\"".$zipname."\"");
header("Content-length: $fsize");
header('Content-Transfer-Encoding: binary');
#header("Cache-control: private"); //use this to open files directly
echo fpassthru($downloadzip); // deliver the zip file
}
fclose ($downloadzip);
I found some headers that work. I don't really know or care why it work, I am just happy it works... I tried a ton of different things, .htaccess files, php.ini / zlib settings.
Here's the answer
http://perishablepress.com/http-headers-file-downloads/
$zipName = 'myfile.zip';
$zipPath = 'mydirectory/' . $zipName;
if (file_exists($zipPath)) {
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$zipName."\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($zipPath));
ob_end_flush();
#readfile($zipPath);
}
Often the issue is caused by extra characters that have been printed or echo'd to the page before you read out the file. Even a space will cause the failure. To fix that issue, call ob_end_clean(); before you read the file which will clear the output buffer and turn off buffering.
But keep in mind you can have nested output buffers, and this will corrupt your download as well (cheers to Vladamir for figuring this out). So to clear the output buffer completely run this before you read your file:
while (ob_get_level()) {
ob_end_clean();
}
This will clear out your entire buffer and you won't have any extra characters to mess up your download.
For those interested i've pasted my download script below. My zip files now download perfectly, and so far this works great.
if (file_exists($zip_file_path)) {
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
//We can likely use the 'application/zip' type, but the octet-stream 'catch all' works just fine.
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename='$zip_file_name'");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($zip_file_path));
while (ob_get_level()) {
ob_end_clean();
}
#readfile($zip_file_path);
exit;
}
Here is what works
$zipName = 'myfile.zip';
$zipPath = 'mydirectory/' . $zipName;
if (file_exists($zipPath)) {
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$zipName."\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($zipPath));
ob_end_flush();
#readfile($zipPath);
}
Well, I presume you know that your $fsize variable is not being written to that header because it's enclosed by quotes.
You could try something like this:
header('Cache-Control: public');
header('Content-Description: File Transfer');
header('Content-Disposition: attachment; filename=\"".$zipname."\"');
header('Content-Type: application/zip');

PHP Download File with Include

How can I use PHP's include function to include a file and then modify the headers so it forces - at least that's how it's called - browsers to download ITSELF (the PHP file). Is it possible to also modify the preset save name, in order to change the extension from *.php to something else?
Thanks in advance!
PHP include function will parse the file. What you want to do is use file_get_contents or readfile.
Here's an example from the readfile documentation:
$file = 'somefile.gif';
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;
}
Change the headers to suit your particular needs. Check out the above links for more info.

File Download Not Working

When I download the original zip it works fine, but when I download it using the below headers and stuff it doesn't work. I know it's better to take this route and tell the browser how to handle the file rather than leave it up to the browser, but I can't get this to work, so I'm tempted to use a header() forward.
$path = $this->tru->config->get('root.path').'/Digital Version of Book for Web.zip';
set_time_limit(0);
header("Cache-Control: public");
header("Content-Description: File Transfer");
header('Content-Type: application/zip');
header("Content-Transfer-Encoding: binary");
header('Content-Disposition: attachment; filename="NewFileName.zip"');
header('Content-Length: ' . filesize($path));
$f = fopen($path, 'rb');
fpassthru($f);
fclose($f);
Edit:
Sorry, what I mean by it doesn't work is that the file downloads in a zip format (all 9.3 MB) but I'm unable to unpackage the zip because it's invalid.
Take a look into the ZIP file using Notepad or another text editor. Check whether there is a PHP error message screwing up the file on the first few lines. It could be a "headers already sent" message or the set_time_limit() call throwing an error due to the script being in safe mode.
Try using readfile(). An example is provided in the PHP Manual.
$file = 'monkey.gif';
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;
}

Categories