Asked  7 Months ago    Answers:  5   Viewed   37 times

Folks, I know there have been lots of threads about forcing the download dialog to pop up, but none of the solutions worked for me yet.

My app sends mail to the user's email account, notifying them that "another user sent them a message". Those messages might have links to Excel files. When the user clicks on a link in their GMail/Yahoo Mail/Outlook to that Excel file, I want the File Save dialog to pop up.

Problem: when I right-click and do "Save As" on IE, i get a Save As dialog. When I just click the link (which many of my clients will do as they are not computer-savvy), I get an IE error message: "IE cannot download file ... from ...". May be relevant: on GMail where I'm testing this, every link is a "target=_blank" link (forced by Google).

All other browsers work fine in all cases.

Here are my headers (captured through Fiddler):

HTTP/1.1 200 OK
Proxy-Connection: Keep-Alive
Connection: Keep-Alive
Content-Length: 15872
Via: **** // proxy server name
Expires: 0
Date: Tue, 20 Oct 2009 22:41:37 GMT
Content-Type: application/vnd.ms-excel
Server: Apache/2.2.11 (Unix) DAV/2 mod_ssl/2.2.11 OpenSSL/0.9.8i mod_python/3.3.1 Python/2.5.2 SVN/1.4.6 mod_apreq2-20051231/2.6.0 mod_perl/2.0.4 Perl/v5.10.0
Cache-Control: private
Pragma: no-cache
Last-Modified: Tue, 20 Oct 2009 22:41:37 GMT
Content-Disposition: attachment; filename="myFile.xls"
Vary: Accept-Encoding
Keep-Alive: timeout=5, max=100

I want IE's regular left-click behavior to work. Any ideas?

 Answers

49

This will check for versions of IE and set headers accordingly.

// assume you have a full path to file stored in $filename
if (!is_file($filename)) {
  die('The file appears to be invalid.');
}

$filepath = str_replace('\', '/', realpath($filename));
$filesize = filesize($filepath);
$filename = substr(strrchr('/'.$filepath, '/'), 1);
$extension = strtolower(substr(strrchr($filepath, '.'), 1));

// use this unless you want to find the mime type based on extension
$mime = array('application/octet-stream');

header('Content-Type: '.$mime);
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.sprintf('%d', $filesize));
header('Expires: 0');

// check for IE only headers
if (preg_match('~MSIE|Internet Explorer~i', $_SERVER['HTTP_USER_AGENT']) || (strpos($_SERVER['HTTP_USER_AGENT'], 'Trident/7.0; rv:11.0') !== false)) {
  header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  header('Pragma: public');
} else {
  header('Pragma: no-cache');
}

$handle = fopen($filepath, 'rb');
fpassthru($handle);
fclose($handle);
Wednesday, March 31, 2021
 
Wilk
answered 7 Months ago
77

The function get_browser() may be used for feature detection.

If you really want to only know the user's browser version you can look at the $_SERVER['HTTP_USER_AGENT'].

A word of caution: although most users won't do this, it is possible to send a different user agent string to the server which might be done to e.g. protect ones privacy. If someone wants to go as far as sending you a user agent to be able to use your website though they'll probably know how to deal with any bugs that may come up.

As a side question, what is the reason you're trying to limit your audience to IE 10 and up? Why not use a library such as Modernizr to fill in functions you might be missing in IE 9?

EDIT: Some info regarding the IE 10 user agent string. You will want to read that so you know what to match your $_SERVER['HTTP_USER_AGENT'] against

Wednesday, March 31, 2021
 
rojo
answered 7 Months ago
86

for everyone who is searching for the same issue.

the TS go the solution :

add exit @ the end (for some reasons firefox needs this)

header("Content-Type: application/pdf");
echo $pdf;
exit;
Saturday, May 29, 2021
 
OMGKurtNilsen
answered 5 Months ago
85
<?php
$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;
}

?>

As you can see Content type is application/octet-steam meaning file is byte by byte encoded. Also the cache headers are set. Then headers are forcefully sent by ob_clean();flush(); and then the file is read.

The file_exists is there to ensure that given file exists. You should also try not not thrust user input as they could easy write names for your php codes and download EACH file. And with ../ in names of the files, even your documents or system files and so on.

Saturday, May 29, 2021
 
Extrakun
answered 5 Months ago
99

Add <meta http-equiv="X-UA-Compatible" content="IE=edge" /> as the first tag of the header to be sure. IE loves to go to compatibility mode even if a HTML5 doctype is present under certain circumstances (especially on intranet files when the computer is in a domain).

You can use the developer tools (F12) to check what mode you are running in.

Wednesday, August 18, 2021
 
shivam
answered 2 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :