Asking user to download file in PHP - php

I am trying to get the browser to prompt the user to download a file. However, after having tried several methods from stack overflow and around the Internet, for some reason all are silently failing. Is it the case that this just isn't possible in modern browsers?
I'm simply wanting the user to download a text (.txt) file from the server. I've tried this code below (and more) to no avail:
header('Content-disposition: attachment; filename=newfile.txt');
header('Content-type: text/plain');
readfile('newfile.txt');
.
header("Content-Type: application/octet-stream");
$file = $_GET["file"] .".txt";
header("Content-Disposition: attachment; filename=" . urlencode($file));
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Description: File Transfer");
header("Content-Length: " . filesize($file));
flush(); // this doesn't really matter.
$fp = fopen($file, "r");
while (!feof($fp))
{
echo fread($fp, 65536);
flush(); // this is essential for large downloads
}
fclose($fp);
I have tried the examples from PHP.NET (none of which are working for me):
http://php.net/manual/en/function.readfile.php
I have the correct permissions set, the file exists and is_readable. I'm now left scratching my head as to why this isn't working. Any help would be great.

I have one solution for you.
Lets assume download.php is the file that downloads the file.
So when the user clicks on the link to download show a confirm dialog, if the user selects yes then re direct the user to download.php or else download will not occur some browsers like chrome starts the download without asking users if they like to download a file or not.

Related

PHP: output file without getting it into memory [duplicate]

I want to serve an existing file to the browser in PHP.
I've seen examples about image/jpeg but that function seems to save a file to disk and you have to create a right sized image object first (or I just don't understand it :))
In asp.net I do it by reading the file in a byte array and then call context.Response.BinaryWrite(bytearray), so I'm looking for something similar in PHP.
Michel
There is fpassthru() that should do exactly what you need. See the manual entry to read about the following example:
<?php
// open the file in a binary mode
$name = './img/ok.png';
$fp = fopen($name, 'rb');
// send the right headers
header("Content-Type: image/png");
header("Content-Length: " . filesize($name));
// dump the picture and stop the script
fpassthru($fp);
exit;
?>
See here for all of PHP's filesystem functions.
If it's a binary file you want to offer for download, you probably also want to send the right headers so the "Save as.." dialog pops up. See the 1st answer to this question for a good example on what headers to send.
I use this
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 use readfile() ( http://www.php.net/readfile )...
But you have to make sure you set the right "Content-Type" with header() so the browser knows what to do with the file.
You can also force the browser to download the file instead of trying to use a plug-in to display it (like for PDFs), I always found this to look a bit "hacky", but it is explained at the above link.
This should get you started:
http://de.php.net/manual/en/function.readfile.php
Edit: If your web server supports it, using
header('X-Sendfile: ' . $filename);
where file name contains a local path like
/var/www/www.example.org/downloads/example.zip
is faster than readfile().
(usual security considerations for using header() apply)
For both my website and websites I create for clients I use a PHP script that I found a long time ago.
It can be found here: http://www.zubrag.com/scripts/download.php
I use a slightly modified version of it to allow me to obfuscate the file system structure (which it does by default) in addition to not allowing hot linking (default) and I added some additional tracking features, such as referrer, IP (default), and other such data that I might need should something come up.
Hope this helps.
Following will initiate XML file output
$fp = fopen($file_name, 'rb');
// Set the header
header("Content-Type: text/xml");
header("Content-Length: " . filesize($file_name));
header('Content-Disposition: attachment; filename="'.$file_name.'"');
fpassthru($fp);
exit;
The 'Content-Disposition: attachment' is pretty common and is used by sites like Facebook to set the right header

Writing into txt file return empty on IE

I have a code that save the data of a form into a TXT file and it seems like Internet Explorer can't read the file once it is closed.
The file is successfully saved in my folder and when I load it from the FTP I can see my values, but when I do readfile() on the "submit", the donwloadable file is empty from data.
Here is my code :
$fp = fopen("plan_de_concepts/". $nom_du_fichier, "w");
$savestring = "$sujet=*|*=$concept1=*|*=$concept2=*|*=$concept3=*|*=$c1mc1=*|*=$c1mc2=*|*=$c1mc3=*|*=$c1mc4=*|*=$c1mc5=*|*=$c2mc1=*|*=$c2mc2=*|*=$c2mc3=*|*=$c2mc4=*|*=$c2mc5=*|*=$c3mc1=*|*=$c3mc2=*|*=$c3mc4=*|*=$c3mc5=*|*=$c4mc1=*|*=$c4mc2=*|*=$c4mc3=*|*=$c4mc5=*|*=$c5mc1=*|*=$c5mc2=*|*=$c5mc3=*|*=$c5mc3=*|*=$c5mc4=*|*=$c5mc5=*|*=$c6mc1=*|*=$c6mc2=*|*=$c6mc3=*|*=$c6mc4=*|*=$c6mc5";
fwrite($fp, $savestring);
fclose($fp);
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Length: ". filesize($nom_du_fichier));
header("Content-Disposition: attachment; filename=". $nom_du_fichier);
header("Content-Type: application/octet-stream; ");
header("Content-Transfer-Encoding: binary");
readfile('plan_de_concepts/'.$nom_du_fichier);
Works great on Firefox and Chrome but IE return empty.. Is there any ways I could force to donwload the "uploaded" version of the file?
You use filesize($nom_du_fichier) and readfile('plan_de_concepts/'.$nom_du_fichier) (note the folder-prefix). I expect only one of the files exists.
It looks like IE is actually using the Content-Length-header where FF is silently ignoring it.
My Content-length was different of the file exact path. Internet Explorer could not find the file to read it.
It should be noted that in the example:
header('Content-Length: ' . filesize($file));
$file should really be the full path to the file. Otherwise content
length will not always be set, often resulting in the dreaded "0 byte
file" problem.
Source : PHP readfile returns zero length file

Zip file downloads, but is invalid?

I use this code to enable users to download a zip file:
if(file_exists($filename)){
header("Content-Disposition: attachment; filename=".basename(str_replace(' ', '_', $filename)));
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Description: File Transfer");
header("Content-Length: " . filesize($filename));
flush();
$fp = fopen($filename, "r");
while (!feof($fp))
{
echo fread($fp, 65536);
flush();
}
fclose($fp);
exit;
}
When the file is downloaded, it only downloads 25,632 kilobytes of data. However the zip file is 26,252 kilobytes ...
Why does the browser get all 25MB but then stop?
I checked the Content-Length header to make sure it was correct and it is...
edit
In firefox, when i download the file, it says 'of 25mb' SO the browser thinks that 25mb is the COMPLETE amount... however, the content-length when echo'd is 26252904?
add this before your code
ob_clean();
ob_end_flush();
Your header('Content-Type ...) calls are useless as only the last one will be sent to the browser.
Downloads are triggered by Content-Disposition: attachment. You should send the actual Content-Type: application/zip if you are sending a zip file.
Finally, your read loop is unnecessary.
Putting it all together, your code should look like this:
if (file_exists($filename)) {
$quoted_filename = basename(addcslashes($filename, "\0..\37\"\177"));
header("Content-Disposition: attachment; filename=\"{$quoted_filename}\"");
header('Content-Type: application/zip');
header('Content-Length: '.filesize($filename));
readfile($filename);
}
Use a single MIME type to represent the data.
In this case using application/octet-stream will do just fine. This is when you dont know the MIME before hand. When you know it, you must put it. Do not use multiple content-type headers.
Usually, when the browser doesn't know how to handle a particular MIME, it will trigger the download process. Further, using Content-disposition: Attachment; .. ensures it.
There exists a simple readfile($filename) which will send out the bytes of the file to the requesting process like below:
header("Content-disposition: attachment;filename=" . basename($filename);
readfile($filename);
I had similar problem. The file downloaded fine in Firefox but not in IE. It appeared that Apache was gzipping the files and IE was not able to ungzip so the files were corrupted. The solution was to disable gzipping in Apache. You can also check if PHP is not gzipping on the fly and disable it too.
For Apache you can try:
SetEnv no-gzip 1
And for PHP, in .htaccess:
php_flag zlib.output_compression on
This answer is by No means a REAL ANSWER.
However i did get it to work... I just set the Content-Length to 30000000. Therefor it thinks the file is bigger than it actually is, and then it downloads it all.
Ugly hack i know, but i couldn't find ANY other way

Create and download a text file using php

Here's what I'm trying to do. I have a series of reports that they also want to be able to download as comma delimited text files. I've read over a bunch of pages where people say simply echo out the results rather than creating a file, but when I try that it just outputs to the page they are on.
I have this in the form of each report
Export File<input type="checkbox" name="export" value="1" />
So on the post I can check if they are trying to export the file. If they are I was trying to do this:
if($_POST['export'] == '1')
{
$filename = date("Instructors by DOB - ".$month) . '.txt';
$content = "";
# Titlte of the CSV
$content = "Name,Address,City,State,Zip,DOB\n";
for($i=0;$i<count($instructors);$i++)
$content .= ""; //fill content
fwrite($filename, $content);
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Length: ". filesize("$filename").";");
header("Content-Disposition: attachment; filename=$filename");
header("Content-Type: application/octet-stream; ");
header("Content-Transfer-Encoding: binary");
readfile($filename);
}
Basically the page refreshes, but no file is pushed for download. Can anyone point out what I'm missing?
EDIT
I think I wasn't entirely clear. This is not on a page that only creates and downloads the file, this is on a page that is also displaying the report. So when I put an exit(); after the readfile the rest of the page loads blank. I need to display the report on this page as well. I think this could also have to do with why it's not download, because this page has already sent header information.
I overlooked the way you are writing out the contents before asking you to try closing the file.
Check the fwrite manual here: http://php.net/manual/en/function.fwrite.php
What you need to do is:
$filename = "yourfile.txt";
#...
$f = fopen($filename, 'w');
fwrite($f, $content);
fclose($f);
and after closing the file, you can now safely send it across for download.
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Length: ". filesize("$filename").";");
header("Content-Disposition: attachment; filename=$filename");
header("Content-Type: application/octet-stream; ");
header("Content-Transfer-Encoding: binary");
readfile($filename);
There are a couple of things:
You really dont need to set the content-type as application/octet-stream. Why not set a more real type as text/plain?
I really dont understand how you want to use the date functionality. please refer to the manual here: http://php.net/manual/en/function.date.php
As correctly pointed out by #nickb, you must exit the script after doing the readfile(..)

PHP output file on disk to browser

I want to serve an existing file to the browser in PHP.
I've seen examples about image/jpeg but that function seems to save a file to disk and you have to create a right sized image object first (or I just don't understand it :))
In asp.net I do it by reading the file in a byte array and then call context.Response.BinaryWrite(bytearray), so I'm looking for something similar in PHP.
Michel
There is fpassthru() that should do exactly what you need. See the manual entry to read about the following example:
<?php
// open the file in a binary mode
$name = './img/ok.png';
$fp = fopen($name, 'rb');
// send the right headers
header("Content-Type: image/png");
header("Content-Length: " . filesize($name));
// dump the picture and stop the script
fpassthru($fp);
exit;
?>
See here for all of PHP's filesystem functions.
If it's a binary file you want to offer for download, you probably also want to send the right headers so the "Save as.." dialog pops up. See the 1st answer to this question for a good example on what headers to send.
I use this
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 use readfile() ( http://www.php.net/readfile )...
But you have to make sure you set the right "Content-Type" with header() so the browser knows what to do with the file.
You can also force the browser to download the file instead of trying to use a plug-in to display it (like for PDFs), I always found this to look a bit "hacky", but it is explained at the above link.
This should get you started:
http://de.php.net/manual/en/function.readfile.php
Edit: If your web server supports it, using
header('X-Sendfile: ' . $filename);
where file name contains a local path like
/var/www/www.example.org/downloads/example.zip
is faster than readfile().
(usual security considerations for using header() apply)
For both my website and websites I create for clients I use a PHP script that I found a long time ago.
It can be found here: http://www.zubrag.com/scripts/download.php
I use a slightly modified version of it to allow me to obfuscate the file system structure (which it does by default) in addition to not allowing hot linking (default) and I added some additional tracking features, such as referrer, IP (default), and other such data that I might need should something come up.
Hope this helps.
Following will initiate XML file output
$fp = fopen($file_name, 'rb');
// Set the header
header("Content-Type: text/xml");
header("Content-Length: " . filesize($file_name));
header('Content-Disposition: attachment; filename="'.$file_name.'"');
fpassthru($fp);
exit;
The 'Content-Disposition: attachment' is pretty common and is used by sites like Facebook to set the right header

Categories