saber酱的抱枕

Fly me to the moon

03/27
08:00
学习

网页通过php实现文件下载

在网页上,如果要通过php实现下载功能,我们可以在超链接里访问这个php,然后由php返回下载的文件。

php端主要是通过设置header来实现,基础的示例如下:

$filename = "test.gif";
header("Content-Type:imge/gif"); //发送指定文件MIME类型的头信息
header("Content-Disposition:attachment; filename=".$filename); //发送描述文件的头信息,附件和文件名
header("Content-Length:".filesize($filename)); //发送指定文件大小的信息,单位字节
readfile($filename); //将文件内容读取出来并直接输出,以便下载


如果遇到文件名里有中文,则有点麻烦,用上面的代码的话可能有的浏览器下载后,文件名是乱码。这里要在Content-Disposition:attachment里设置文件名时加以处理。具体代码如下:

//生成支持中文的filename,兼容IE11
$ua = $_SERVER["HTTP_USER_AGENT"];
$encoded_filename = urlencode($filename);
$encoded_filename = str_replace("+", "%20", $encoded_filename);
if(preg_match("/MSIE/", $ua) || preg_match("/Trident\/7.0/", $ua)){
    header('Content-Disposition: attachment; filename="' . $encoded_filename . '"');
} else if (preg_match("/Firefox/", $ua)) {
    header('Content-Disposition: attachment; filename*="utf8\'\'' . $filename . '"');
} else {
    header('Content-Disposition: attachment; filename="' . $filename . '"');
}

参考来源:PHP下载文件名中文乱码解决方法


上面文章里还提到了向浏览器流式输出内容。主要是把Content-Type设置为流式:

header('Content-Type: application/octet-stream');

之后当然也没有读取本地文件的过程,而是直接输出内容。例子里是输出了一个字符串,实际上也可以直接输出包含有文件内容的变量。比如我最近做的ikanman下载器里的一部分:

// 获取图片
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_REFERER, "http://www.ikanman.com");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$cexecute = curl_exec($ch);
curl_close($ch);
// 设置header
header('Content-Type: image/webp');
header('Content-Disposition: attachment; filename="' . $filename . '"');
 Header("Accept-Ranges: bytes"); 
header('Content-Length:'.strlen($cexecute));
// 输出图片
echo $cexecute;

远程获取一个图片,然后把图片直接发送给浏览器。这样也是可以的。

注意:这种方式没有先把文件存到服务器,而是直接输出到浏览器,这种情况下比较推荐模拟a标签点击来发送请求,用ajax的话可能获取的是乱码内容,也不会引发下载。


上面说了php如何向浏览器发送文件来下载,下面说下浏览器里怎么向php发送下载文件的请求,并成功实现下载。

这里有一个我栽过的坑,就是用ajax发起下载请求。这是不行的,因为ajax接收的数据只会是纯字符串形式,就算php成功发送过来一个文件,这个文件也只会以字符串形式保存在ajax的返回内容里,而不会引起浏览器的下载。

用img标签的src也不行,接收到的文件会被解析为一个损坏的图片(因为文件内容不是图片url的形式),也不会引起浏览器的下载。

那么我们可以用那些方式让浏览器发起文件下载呢?

比较简单的是创建一个a标签,设置其href为php的链接。点击这个超链接就可以下载图片了(或者js模拟鼠标点击),很方便。

此外我们还可以构建一个form,将form的action地址设置为php的链接,然后提交这个form,一样可以进行下载。

不太推荐的是创建一个iframe,然后将其src设置为php的链接,设置之后会马上发起请求,下载图片。在不刷新页面又要下载多个文件时,尽量避免使用iframe。

网页通过php实现文件下载