一步一步分析解决PHP文件下载直接在网页中打开问题
不知道大家看PHP函数的时候,有没有注意到这样一个句话 -
认识到一点很重要,即必须在任何实际的输出被发送之前调用 header() 函数
(在 PHP 4 以及更高的版本中,您可以使用输出缓存来解决此问题)
哎~~ 一个困扰了我一天的问题,最终以
这句话结束。
在使用TestLink的时候,发现Testlink中的附件在选择下载都是直接在网页中打开,图片,word都显示成乱码。各种困惑,开始了以下的调研:
1)查看代码,未见端倪。
我 直接检查PHP代码中实现下载的代码,查看Header是否正确:
@ob_end_clean(); require_once("../../config.inc.php"); require_once("../functions/common.php"); require_once("../functions/attachments.inc.php"); testlinkInitPage($db,false,false,"checkRights"); $args = init_args(); if ($args->id) { $attachmentRepository = tlAttachmentRepository::create($db); $attachmentInfo = $attachmentRepository->getAttachmentInfo($args->id); if ($attachmentInfo && checkAttachmentID($db,$args->id,$attachmentInfo)) { $content = $attachmentRepository->getAttachmentContent($args->id,$attachmentInfo); if ($content != "") { @ob_end_clean(); header("Pragma: public"); header("Cache-Control: "); if (!(isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on" && preg_match("/MSIE/",$_SERVER["HTTP_USER_AGENT"]))) header("Pragma: no-cache"); header("Content-Type: ".$attachmentInfo["file_type"]); header("Content-Length: ".$attachmentInfo["file_size"]); header("Content-Disposition: attachment; filename="{$attachmentInfo["file_name"]}""); header("Content-Description: Download Data"); echo $content; exit(); } } }
2)各种尝试,奈何是南辕北辙。
看完代码第一反应是没错啊.
header("Content-Disposition: attachment; filename="{$attachmentInfo["file_name"]}""); //这个指名了文件时附件而不是直接在网页中打开 header("Content-Type: ".$attachmentInfo["file_type"]);//这个也是或去了文件的类型
下一反应是,难道是 content-Type出问题了,让我们直接hard code改之成为二进制流试试吧
header("Content-Type: "application/octet-stream); header("Content-Disposition: attachment; filename="{$attachmentInfo["file_name"]}"");
结果非常的失望,图片还是在浏览器中打开,显示乱码。
好吧,我选择一个不存在的content-Type试试吧,这样浏览器肯定是不认识的。尝试的结果,还是失败。
header("Content-Type: "application/download);
3)山不起就我,我就去就山。
各种尝试无果,我开始怀疑我是不是Header的函数写错了,于是,我开始了新的尝试,建立一个test.php文件,就测试这个download。事实证明,test.php运行非常正常,点击下载链接,图片正确下载。
<?php //文件 test.php //=================================文件下载 if ($_GET["down"]) { $file_name = "a.png"; $file_dir = "./"; if (!file_exists($file_dir . $file_name)) { //检查文件是否存在 echo "文件找不到"; exit; } else { // 输入文件标签 Header("Content-type: application/octet-stream"); //指定以下输出的字符将以下载文件形式保存在客户端 Header("Accept-Ranges: bytes");//指定下载的文件大小单位 Header("Accept-Length: " . filesize($file_dir . $file_name));//指定下载的文件大小 Header("Content-Disposition: attachment; filename=" . $file_name); //指定下载的文件名.扩展名 $file = fopen($file_dir . $file_name, "r"); // 打开文件 // 输出文件内容,除了下载文件编码之外该页面不能有任何其他输出 echo fread($file, filesize($file_dir . $file_name)); fclose($file); exit; //防止读取下面的其他输出 } } ?> <a href="?down=1">下载</a>
4)从果推因,柳暗花明。
为什么测试的程序工作,而之前的程序不工作呢?
通过Chrome的dev tool,我又得到了一个有意思的发现 -
无论我在程序中设定的Content-Type 是什么,实际上Content-Type并没有生效。
从结果的表现,我们可以分析出来 - 我们调用的Header 并没生效。
我们回去查看源代码,问题出在哪里呢?
回到了前言!!
认识到一点很重要,即必须在任何实际的输出被发送之前调用 header() 函数
(在 PHP 4 以及更高的版本中,您可以使用输出缓存来解决此问题)
强烈怀疑是在这个函数testlinkInitPage($db,false,false,"checkRights")中有提前的Echo!!所以 header失效了。。
本着这个原则,我把Header的生命提前,程序终于work了。。
@ob_end_clean(); header("Pragma: public"); header("Cache-Control: "); require_once("../../config.inc.php"); require_once("../functions/common.php"); require_once("../functions/attachments.inc.php"); testlinkInitPage($db,false,false,"checkRights"); $args = init_args(); if ($args->id) { $attachmentRepository = tlAttachmentRepository::create($db); $attachmentInfo = $attachmentRepository->getAttachmentInfo($args->id); if ($attachmentInfo && checkAttachmentID($db,$args->id,$attachmentInfo)) { $content = $attachmentRepository->getAttachmentContent($args->id,$attachmentInfo); if ($content != "") { @ob_end_clean(); if (!(isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on" && preg_match("/MSIE/",$_SERVER["HTTP_USER_AGENT"]))) header("Pragma: no-cache"); header("Content-Type: ".$attachmentInfo["file_type"]); header("Content-Length: ".$attachmentInfo["file_size"]); header("Content-Disposition: attachment; filename="{$attachmentInfo["file_name"]}""); header("Content-Description: Download Data"); echo $content; exit(); } } }
- 上一篇: nginx/php-fpm 访问php文件直接下载而不运行
- 下一篇: Oracle会话查询等