I'm offering a sermon downloading site and I have a user experiencing an issue with his download. Anyone have any ideas on how I can improve this code, or perhaps send better headers...
$path = "http://www.domain.com/sermon_files/".date("Y", $array["preached"])."/".$array["filename"];
$corePath = "/home/user/public_html/sermon_files/".date("Y", $array["preached"])."/".$array["filename"];
if (!file_exists($corePath)) {
echo "An error has occured with this download.";
} else {
header("Pragma: public"); // required
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private", false);
header("Content-Type: audio/mp3");
header("Content-Disposition: attachment; filename=\"".$array["title"]."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($corePath));
readfile($path);
exit();
}
Take a look at this thread, I had similar problems: PHP: Force file download and IE, yet again. Also consider using Fiddler to capture the exact HTTP headers that are being sent to the client.
Combine your PHP ( ? ) code with a server with X-Send available, lighttpd has it so does apache
Watch out for Expires: 0 on downloads. This messes with IE6's tiny little brain and makes it think there is no data to save/open. Try expiring in a minute from access and see if that fixes the problem. Otherwise, tell us exactly what the problem is.
Related
I am using Zend and have some files outside of the webroot that I would like to be able to serve up. I have tried two approaches, both of which work in all browsers except for versions of IE 8 or lower.
The two (working) approaches that I have tried are the following:
// Approach #1
header('Content-Type: application/pdf');
header("Pragma: ");
header("Content-Disposition: attachment; filename=\"$filename\"");
//header('Content-Transfer-Encoding: binary');
header("Pragma: no-cache");
header("Expires: 0");
readfile($file);
// Approach #2
$this->getResponse()
->setHeader('Content-Disposition', "attachment; filename=$filename")
->setHeader('Content-type', 'application/x-pdf');
fpassthru($file);
Like I said, both approaches work in modern browsers (even IE9) but not in older versions of IE. The error I am getting is the following: http://cl.ly/image/1G3x370b1s09
I have looked into several posts on this topic and tried more different combinations of headers than I can even count. Is there a more bulletproof way of handling this functionality that wont cause issues with older browsers?
Thanks!
I've fought with this before and I think it stems from caching headers.
There's three: Expires, Cache-Control (HTTP 1.1), and Pragma (HTTP 1.0). My experience has been the older versions of IE like to see all three of these headers. Try using the following prior to any other headers and content you send:
header("Cache-control: no-cache");
header("Pragma: no-cache");
header("Expires: -1");
This article from Microsoft goes in to more discussion about the caching headers.
This is what I have done in the past to get it to work:
$file = $fileInfo->openFile('r');
header("Pragma: public");
header("Cache-Control: public");
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename="'.$file->getFilename().'"');
print $file->fpassthru()
Against my will I gave up on trying to fight with headers and completely changed the way I am handling file downloads. When a user requests a file now, it is temporarily hashed, copied to an area that the web-server can see, the user is redirect to that file and once they leave the download area the file is deleted. If they go inactive the file is deleted automatically at a set interval.
Thank you for all of the input kulishch and how ironic is it that you are from Minnesota as well!? Happy Holidays!
-- Nicholas
Follwing the advice at http://support.microsoft.com/default.aspx?scid=KB;EN-US;q316431&, these headers worked for me:
header("Cache-control: max-age=3600, must-revalidate");
header("Pragma: public");
header("Expires: -1");
I always get caught out by this! :(
I'm using the following the PHP script to download a 20mb file:
(filepath & filename are set earlier in the script)
$fullPath = $filepath.$filename;
if ($fd = fopen($fullPath, "r")) {
// http headers for zip downloads
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=\"".$filename."\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($fullPath));
while(!feof($fd)) {
echo(fread($fd, 1024*8));
flush();
if (connection_status()!=0) {
fclose($fd);
die();
}
}
}
fclose($fd);
exit();
It works fine if the download finishes, but if the download is canceled by the user, and they click on the link to re-download, the server is completely unresponsive for several minutes, and then the download will begin. It seems like it is waiting for the script to time out...but isn't the "if (connection_status()!=0)..." part supposed to kil the script?
Any help is much appreciated! Thank you in advance!
I think you're over-engineering your solution somewhat. Try using readfile() instead of your fopen/fread loop.
Better yet, unless there's a compelling reason why you need PHP to mediate the file transfer, don't use a PHP script at all and simply provide a direct link to the file in question.
The best code is always the code you don't have to write.
I'm using a script to download video, but it take lot of time to download. Are there any processes or other scripts that could help me?
// set headers
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: $mtype");
header("Content-Disposition: attachment; filename=\"$asfname\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . $fsize);
// download
// #readfile($file_path);
$file = #fopen($file_path,"rb");
if ($file) {
while(!feof($file)) {
print(fread($file, 1024*100));
flush();
if (connection_status()!=0) {
#fclose($file);
die();
}
}
#fclose($file);
}
Using the readfile() function (as you originally had) will allow you to spool directly from the file to output, rather than using a chunking loop and printing as you're doing. So why have you chosen to do this chunk loop?
As above, readfile() is one way.
The other, even more preferred method depends on your webserver. NginX, Lighttpd and there's also a module for Apache, allows you to pass a header with a filepath/name to the server, and it will send the file directly from the server itself, and so not need to use PHP resources to do it. If thats not possible, then readfile() is the best you probably have - if you can't just give someone a direct URL to download it.
For the past 3 months my site has been using a PHP file handler in combination with htaccess.
Users accessing the uploads folder of the site would be redirected to the handler as such:
RewriteRule ^(.+)\.*$ downloader.php?f=%{REQUEST_FILENAME} [L]
The purpose of the file handler is pseudo coded below, followed by actual code.
//Check if file exists and user is downloading from uploads directory; True.
//Check against a file type white list and set the mime type(); $ctype = mime type;
header("Pragma: public"); // required
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false); // required for certain browsers
header("Content-Type: $ctype");
header("Content-Disposition: attachment; filename=\"".basename($filename)."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filename));
readfile("$filename");
As of yesterday, the handler started returning garbled files, unreadable images, and had to be bypassed. I'm wondering what settings could have gone awry to cause this.
-EDIT-
Problem found, but not resolved. Including a path to a php library I was using for integrating with Wordpress was corrupting the files. Removing that block of code solves the corruption issue but leaves the files accessible without the desired authentication.
#include_once($_SERVER['DOCUMENT_ROOT'].'/wp-blog-header.php');
if(!is_user_logged_in())
{
auth_redirect(); //Kicks the user to a login page.
}
//resume download script
Maybe more tests will reveal the problem...
if ( !isset($filename) ) {
die('parameter "filename" not set');
}
else if ( !file_exists($filename) ) {
die('file does not exist');
}
else if ( !is_readable($filename) ) {
die('file not readable');
}
else if ( false===($size=filesize($filename)) ) {
die('stat failed');
}
else if ( headers_sent() || ob_get_length()>0) {
die('something already sent output.');
}
else {
$basename = basename($filename);
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false); // required for certain browsers
header("Content-Type: $ctype");
header("Content-Disposition: attachment; filename=\"".$basename."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".$size);
readfile($filename);
}
How are the files corrupted? Truncated? 0-byte? Completely different content? Random sections replaced with garbage?
Is it possible the server's PHP memory limit has been lowered? readfile() will buffer the whole file in memory before outputing it. Therefore a 40meg file will fail is the memory limit is 39.9999, kind of thing.
For streaming a file to the user, it's best to NOT use php's own "dump file to browser" functions, as they're all subject to the memory limit. It's best to do an fopen/fwrite/fclose loop and spit the file out in small manageable chunks (4k, 16k, etc...).
I'm struggling with an odd error. I have a simple web app that grabs stuff from a DB then outputs it as a downloadable csv file. It works on firefox and chrome, but IE fails to recognize it as a csv file (thinking it is a html fle) and when I click save I get the error, "Unable to download {name of file} from {name of site}. Unable to open this internet site. ..."
Code:
session_start();
//some logic goes here...
//generate csv header
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=exportevent.csv");
header("Pragma: no-cache");
header("Expires: 0");
echo "Event: " . $event_title . "\n";
//print the column names
echo "Last Name, First Name, Company \n";
while($row = mysql_fetch_assoc($result))
{
echo $row['atlname'] . ',' . $row['atfname'] . ',' . $row['atcompany'] . "\n";
}
I've played around with the content-type a whole bunch, but that had no effect.
Update: I've tried text/csv, application/vnd.ms-excel (and variations of this), text/plain, and some others that I now forget with no luck.
This is IE8 btw.
Update 2: The connection is over SSL.
Don't we love IE? :)
Try using those headers:
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false);
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"exportevent.csv\";" );
header("Content-Transfer-Encoding: binary");
I think that the octet-stream content type forces IE to download the file.
We recently ran into this problem ourselves. See this MSKB article
These are the headers we ended up having to use to get it to work over SSL.
header("Expires: Sat, 01 Jan 2000 00:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"$file_name\";");
header("Content-length: " . strlen($csv_string));
I've had success with the following:
header("Content-type: application/vnd.ms-excel");
header("Content-disposition: attachment; filename=File.csv");
Setting the type to application/vnd.ms-excel seemed to do the trick in my case. This is all in a file that is opened by submitting a form using
target="_blank"
The only extra code I had to add for IE to work with SSL was: header("Pragma: public");
So my headers look like this now:
header("Pragma: public");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=some_filename.csv");
We have just had the same issue and after adding many headers and getting a working link I then removed them one by one and found the key one for us was
"Cache-Control: public"
so in the end we just had
header("Cache-Control: public");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=some_filename.csv");
which worked fine.
Try setting your content type to text/csv instead of application/octet-stream.
Since application/octet-stream is a generic binary mime type (and doesn't match the '.csv' extension), Internet explorer might be ignoring it and computing the mime type based on the file extension.
After using Javascript it will solve your problem.
Use this for IE,
var IEwindow = window.open();
IEwindow.document.write('sep=,\r\n' + CSV);
IEwindow.document.close();
IEwindow.document.execCommand('SaveAs', true, fileName + ".csv");
IEwindow.close();
For more information i have written tutorial on that,
see - Download JSON data in CSV format Cross Browser Support
Hope this will be helpful for you.
The solution for me was:
header_remove();
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename=brokerlist.csv');
echo $content;
Did you try the Content-type: text/csv ?
Some time ago I've got a problem with IE6 opening pdf files, and crashing when AdobeReader 6.0 was installed and tried to open file in browser window. Than I found somewhere this header:
header('Content-Type: application/force-download');
And it solved the problem, every pdf file was downloaded and opened in Adobe instead of IE.
This simply doesn't make sense. I tried the accepted answer, all the other answers in here, and it didn't work for me. I tried their permutations, and somehow I managed to make it work in IE like so:
header("Pragma: public");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Type: application/vnd.ms-exce");
header("Content-Disposition: attachment; filename=coupons.csv" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . strlen($csv));
echo $csv;
die();
One thing I did is to empty the cache every freaking time I test the code. And it still doesn't make sense. Just in case someone might need this desperately ;)
If you are trying to accomplish this task (getting a CSV file to download in IE8) using Salesforce.com (in which case your front-end is Visualforce and you can't set all of the headers, only some of them), here's what you need:
<apex:page cache="true"
contentType="application/octet-stream#myAwesomeFileName.csv"
showHeader="false" sidebar="false" standardStylesheets="false">
<apex:outputText value="{!csvContent}" escape="false"/>
</apex:page>
The key pieces here are cache=true, which, in conjunction with the default expires=0 attribute, achieves the following headers:
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
And then the contentType of application/octet-stream --- doing text/csv fails for IE8.