《前言》
不知道大家看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();
}
}
}