在web开发过程中,文件的上传下载是必须的,同时竟会经常出现一些无厘头的bug,接下来讲一些php文件下载需要注意的地方。
(1)php解释器配置文件是必不可少的,在php.ini配置文件中 开启phpinfo.dll扩展
extension=php_fileinfo.dll 去掉前面的;即可
(2)如果经验丰富的话,我们知道,一些简单的文件下载,如docx,rar等一些文件格式的,由于它是不能被浏览器默认识别的MIME类型,因为在实现下载时只需要用<a href="....../docx/xxx.docx"></a>链接地址指向文件存储路径即可,浏览器会自动弹出打开或下载的提示框,这样客户端便可以实现下载,但是对于图片格式,如gif、jpg
还有视频格式,如MP4、wmv等文件格式的,当我们还是使用上面那种方式时,发现浏览器会直接打开它,并不会提示下载,这是因为这些文件类型是浏览器可是默认识别的。
这时,我们就需要通过另一种方式来实现了。
上述通过<a>实现的下载实际上是不安全的,因为将文件路径信息暴露在了浏览器中。合适的做法应该是服务器端通过header将头信息回传给浏览器,该函数接受一个头信息作为参数。文件下载需要发送的头信息包括以下三部分,通过调用三次header()函数完成
header(‘Content-Type:imge/gif'); //发送指定文件MIME类型的头信息
header(‘Content-Disposition:attachment; filename=”test.gif”‘); //发送描述文件的头信息,附件和文件名
header(‘Content-Length:3390′); //发送指定文件大小的信息,单位字节
有了这三个头信息,浏览器就不会直接打开文件,而是让浏览器以下载形式打开文件。 接下来就需要将文件内容输出到浏览器,以便浏览器进行下载,使用php中的readfile()函数,可以文件直接输出。
(3)下面讲一下tp框架中的Http.class.php中的download方法(个人感觉还是很好用的!)
public static function download($filename, $showname = '', $content = '', $expire = 180)
{
ob_end_clean();
if (is_file($filename)) {
$length = filesize($filename);
} elseif (is_file(UPLOAD_PATH . $filename)) {
$filename = UPLOAD_PATH . $filename;
$length = filesize($filename);
} elseif ('' != $content) {
$length = strlen($content);
} else {
E($filename . L('下载文件不存在!'));
}
if (empty($showname)) {
$showname = $filename;
}
$showname = basename($showname);
if (!empty($filename)) {
$finfo = new \finfo(FILEINFO_MIME);
$type = $finfo->file($filename);
} else {
$type = "application/octet-stream";
}
//发送Http Header信息 开始下载
header("Pragma: public");
header("Cache-control: max-age=" . $expire);
//header('Cache-Control: no-store, no-cache, must-revalidate');
header("Expires: " . gmdate("D, d M Y H:i:s", time() + $expire) . "GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s", time()) . "GMT");
header("Content-Disposition: attachment; filename=" . $showname);
header("Content-Length: " . $length);
header("Content-type: " . $type);
header('Content-Encoding: none');
header("Content-Transfer-Encoding: binary");
if ('' == $content) {
readfile($filename);
} else {
echo ($content);
}
exit();
}
前面的获取文件内容,设置下载附件名字,获取文件大小、类型在这里不做过多介绍,这些大家都能看懂,主要说一下下面的几个header()函数。
header("Pragma: public");
pragma是HTTP 1.0协议,通常在不缓存情况下使用,通常为 pragma:no-cache 这儿设置public 应该是可以在任何地方缓存
header("Cache-control: max-age=" . $expire);
上述是Cache-control可以设定的一些参数值,这里设置的是 max-age:内容能被缓存的时间,这个参数由调用时方法参数给定。
header("Last-Modified: " . gmdate("D, d M Y H:i:s", time()) . "GMT");
客户端通过浏览器访问服务器第一次请求时,服务器端的返回状态是200,内容是客户端请求资源,同时还会有一个Last-Modified的属性标记此文件在服务期端最后被修改的时
间,当浏览器第二次访问服务器的时候,浏览器会发送一个If-Modified-Since 报头,询问该时间之后文件是否有被修改过,如果没有修改的话,服务器会返回一个304状态吗,
内容为空,这样就可以节省数据流量。
header("Expires: " . gmdate("D, d M Y H:i:s", time() + $expire) . "GMT");
设定浏览器缓存时间,在浏览器中有缓存区。这时当浏览器向同一url发送请求时,浏览器有两种处理方式:
1.直接在缓冲区内取资源:如果在服务器端设置了expires,max-age时,浏览器不会在有效时期内再去向服务器发送请求,而是直接从缓存区取资源
2.向浏览器发送请求资源。这种情况发生在在头部设置expires:no-cache时,或者是设置了 Expires,max-age但浏览器行为是 ' 刷新' 或 '重载'时候。
header('Content-Encoding: none');
header("Content-Transfer-Encoding: binary");
http协议中的编码协议和数据传输编码协议,第一个none表示编码内容默认,通常它有gzip,deflate,compress三种方式。第二个表示使用二进制方式传输。
下面说一个使用http.class.php中的download方法过程中需要注意的:
(1)下载文件名最好不要用中文命名,经常会出现找不到下载文件的问题。
(2)如果下载完成后,例如下载了一个视频文件,但是打开时显示无效文件。这时下载过程中的错误,这时候尝试在执行下载操作前,清空浏览器缓存区。使用ob_end_clean()函数。这个问题在使用tp框架中的verify时,验证码图片加载不出来也同样适用。
(3)Http:download($filename,$showname); 中的第二个参数一定要标识文件后缀名,第一个参数为文件所在路径。