I've been going around in circles trying to serve a PDF securely using variations of fopen() and readfile(). Most methods work fine in Firefox and Chrome whether I'm on Windows/OSX/iOS but no method will work with Internet Explorer or Safari. I've tried all sorts of header variations based on what others have suggested but no luck.
Here is my code:
<?php
session_start();
if ($_SESSION['user_is_logged_in'] === true || $_SESSION['admin_user_is_logged_in'] === true) {
$name = $_GET['name'];
$path = "www.mypath.com/$name";
$fp = fopen($path, 'rb');
header("Content-Type: application/pdf");
header("Content-Length: " . filesize($path));
//Experimented with this but all it does is force a download instead of opening in browser.
//header("Content-Disposition: attachment; filename=".$name);
readfile($path);
//alternately tried
//passthru($fp);
}
else {
echo "You do not have permission to view this file. Please <a href='../../index.php'>log in</a>.";
}
exit;
?>
If I use Content-Disposition in the header the result is any PDF downloaded in Safari or IE is 0KB and not readable. Without content-disposition IE and Safari just hang on a blank page.
These are some advices I would like to share:
Try to use localpaths ie. windows (c:\folder\resource.pdf) or linux (/var/www/project/resouce.pdf).
Print your $path and check that is a file generated correctly.
Then try this piece of code, that is which I use:
<?php
header("Content-type: application/pdf");
header("Content-Disposition: attachment; filename={$this->filename}.pdf");
readfile($path);
unlink($path);
Related
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.
I have this code on http://domain.com/download.php
<?php
$remote_direct_link = "http://example.com/path/to/movie.mp4";
$filename = "movie-test.mp4";
$ctype="application/octet-stream";
header("HTTP/1.0 302 Found");
header("Content-Type: ".$ctype);
header("Connection: close");
header("Content-Disposition: attachment; filename=\"".basename($filename).'"');
header("Location: " . $remote_direct_link);
?>
When i access domain.com/download.php on browser, i want it to be forced downloaded file movie-test.mp4 with dialog on browser. But unfortunately, it always redirect to http://example.com/path/to/movie.mp4 and play it on browser. How can it do it ? Is there something wrong on my code.
Thanks
First of all, it seems like a really bad idea to download a file from a remote destination and then send it to the client. It gives a lot of overhead data transfer. The client has to wait till you downloaded the file, before you can serve it. Large files take a long time. Also, you have a new issue if the remote destination is unreachable.
That being said, you should pass the contents of the file, instead of redirecting.
<?php
$remote_direct_link = "http://example.com/path/to/movie.mp4";
$filename = "movie-test.mp4";
$ctype="application/octet-stream";
header("HTTP/1.0 302 Found");
header("Content-Type: ".$ctype);
header("Connection: close");
header("Content-Disposition: attachment; filename=\"".basename($filename).'"');
echo file_get_contents($remote_direct_link); // instead of redirection
?>
But a better and simpler approach would simply be having the file locally. It enables you to serve the file faster and you save half of the data transfer.
<?php
$file_contents = file_get_contents('../outside_http/movie.mp4');
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"movie-test.mp4\"");
echo $file_contents;
The last line in your code redirects the user to the external URL, which causes all your other code to be ignored. Instead, you may want to try using readfile() function like;
readfile($remote_direct_link);
<?php
header('Content-disposition: attachment; filename=Booking.pdf');
header('Content-type: application/pdf');
readfile('http://mysite.com/Booking.pdf');
?>
why is the Booking.pdf file downloaded empty!??
mac and windows both say:
The file “Booking.pdf” could not be opened because it is empty.
checked google and stackoverflow, can't find relative info... has anyone experienced this before?
ps: I only found this forum post:'The online issue is a bit off topic I think, but is generally due to loading the PDF to a server in the ASCII mode of FTP rather than binary. That creates a corrupt file. Be sure to turn on binary transmission', but this is not true in this case as i can display the same pdf file in an iframe and it is not blank/empty.
You need to change
readfile('http://mysite.com/Booking.pdf');
To
readfile(__DIR__ . '/Booking.pdf');
Example
$file = __DIR__ . '/test.pdf' ;
header('Content-disposition: attachment; filename=Booking.pdf');
header('Content-type: application/pdf');
header("Content-length: ".filesize($file));
readfile($file);
This has been posted, but I've tried lot of solutions found on SO and more (like this: http://davidwalsh.name/php-force-download)
I basically have this:
{
$filePath = '../public/myfile.png';
$fileName = basename($filePath);
$fileSize = filesize($filePath);
if (!is_file) {
die("File not found");
} else {
header("Content-Description: File Transfer");
header('Content-Type: application/octet-stream');
header("Content-Disposition: attachment; filename= " . $fileName);
header("Content-Transfer-Encoding: binary");
readfile($filePath);
}
}
Files are recognized and downloaded, but .PNGs are empty and .DOCs are corrupted (and Word asks me to fix the file, then it's ok). I have tried also PDFs, and no problem with that.
I trues to put all sort of options (Pragma, Cache-Control, Expires, Content-Length, etc.), still downloaded files but corrupted in some way...
Did you ever had my problem? Please consider I'm on IIS 7.5
Thanks in advance
Open the downloaded files with a plain text editor like Notepad++. At the top you will find a PHP Error notice, it will tell you what's going wrong.
The error is probably "session already send". Then add ob_start(); at the beginning of your script.
After some testing with the code below I've come to this conclusion:
The script works fine for AVI files in both firefox and chrome (if I change the content type to video/avi of course)
The script works fine for MKV files in Chrome. Firefox throws an error message in my face though (see below)
I've downloaded a 20 mb big test file (test7.mkv) from http://matroska.org/downloads/test_w1.html to test with a smaller file. Strangely Firefox can download that file and it seems to work. However if I try it on a 6 gb big file I get the firefox error you see below
Edit: Downloaded a 700 mb file, that didnt play at all in SM player (exitcode 1) but played fine in VLC player. I'm currently looking for a way to find how and where the file might be damaged.
My obvious question is: what am I doing wrong? How to properly download a mkv file in firefox or better in any common browser for that matter. I cant find anything helpful on google but maybe I'm just looking in the wrong places.
Firefox Error when trying to download a mkv file:
Corrupted Content Error
The page you are trying to view cannot be shown because an error in the data transmission was detected.
The page you are trying to view cannot be shown because an error in the data transmission was detected.Please contact the website
owners to inform them of this problem.
function download($file)
{
$path = $_SERVER['DOCUMENT_ROOT']; //<-- added the relative part after that
$fullPath = $path.$file;
set_time_limit(0);
if ($fd = fopen ($fullPath, "r"))
{
$fsize = filesize($fullPath);
$path_parts = pathinfo($fullPath);
$ext = strtolower($path_parts["extension"]);
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Type: video/x-matroska");
header("Content-Disposition: attachment; filename=\"$file\"");
header("Content-Transfer-Encoding: binary");
header("Content-length: $fsize");
$file = #fopen($fullPath,"rb");
if ($file)
{
while(!feof($file))
{
print(fread($file, 1024*8));
flush();
if (connection_status()!=0)
{
#fclose($file);
die();
}
}
#fclose($file);
}
exit;
}
}
the absolute path to an mkv file on the server
Firefox may be choking on the absolute path in the filename header value.
Try specifying mere a file name instead:
$filename = pathinfo($filePath, PATHINFO_BASENAME);
header("Content-Disposition: attachment; filename=\"$filename\"");
Solution:
I should've checked if the variables I define are set to the expected values. Turns out filesize() returns a signed int with 32 bit.
For now I just took the function that's shown here: https://stackoverflow.com/a/5502328/1232791
An other possible solution would be to not define the content length header. With this solution the client wont know how long his download is going to take though.