PHP实现点击a标签的href做链接时,直接保存文件(任何类型),而不是通过浏览器直接打开下载的文件
2013-01-24 00:27
1321 查看
之前做项目遇到这样一个问题,就是在php环境下,用a标签的href链接到一个资源,比如是mp3或者lrc文件时,点击之后不是出现保存文件的提示,而是调用本地程序打开文件或者直接在浏览器上解析。网上说可以全部做成rar格式的文件,这个一方面不方便,有些情况下也不可能完全这样做,还有实际上,做过测试会发现,在content-type:text/html的情况下,即时是rar有时也会被浏览器直接解析,无法实现下载的功能,那这个问题是不是就无解了呢?答案是否定的,几番搜索+测试,终于发现了一个可行的解决方案,就是在点击a标签的链接之后,不是直接请求资源,而是对header做一下预处理,再去readfile就可以了。下面附上相关的测试代码:
一、
index.php中:
process.php中:
这是最简单的方法,但是有个问题:如果请求的路径中包含中文,那么下载的文件名有可能就是乱码。
二、
针对上面问题的解决方案,index.php中:
process.php中:
输出的时候,如果是Apache+PHP,那么还需要发送到Apache的输出缓冲区,最后才发送给用户。而对于Nginx+fpm,如果它们分开部署的话,那还会带来额外的网络IO。
三、
现在貌似没有问题了,但是readfile还是有问题的,虽然PHP的readfile尝试实现的尽量高效,不占用PHP本身的内存,但是实际上它还是需要采用MMAP(如果支持),或者是一个固定的buffer去循环读取文件,直接输出。
那么能不能绕过PHP这层呢,直接由webserver把文件发送给用户呢?可以的,我们可以使用Apache的module mode_xsendfile,让Apache直接发送这个文件给用户。
代码实现如下:(process.php)
最后,如果愿意的话,可以先判断后缀,因为有时候图片当成文件下载也会引起一些不方便的:
一、
index.php中:
<?php echo "<a href='process.php?filename=halo.mp3'>下载</a>" ?>
process.php中:
<?php header("Content-type: application/octet-stream"); header('Content-Disposition: attachment; filename="'. basename($_GET['filename']).'"'); header("Content-Length: ". filesize($_GET['filename'])); readfile($_GET['filename']); ?>
这是最简单的方法,但是有个问题:如果请求的路径中包含中文,那么下载的文件名有可能就是乱码。
二、
针对上面问题的解决方案,index.php中:
<?php echo "<a href='process.php?filename=halo光环.mp3'>下载</a>" ?>
process.php中:
<?php header("Content-type: application/octet-stream"); //处理中文文件名 $ua = $_SERVER["HTTP_USER_AGENT"]; $encoded_filename = urlencode($_GET['filename']); $encoded_filename = str_replace("+", "%20", $encoded_filename); if (preg_match("/MSIE/", $ua)) { header('Content-Disposition: attachment; filename="' . $encoded_filename . '"'); } else if (preg_match("/Firefox/", $ua)) { header("Content-Disposition: attachment; filename*=\"utf8''" . $_GET['filename'] . '"'); } else { header('Content-Disposition: attachment; filename="' . $_GET['filename'] . '"'); } header("Content-Length: ". filesize($_GET['filename'])); readfile($_GET['filename']); ?>
输出的时候,如果是Apache+PHP,那么还需要发送到Apache的输出缓冲区,最后才发送给用户。而对于Nginx+fpm,如果它们分开部署的话,那还会带来额外的网络IO。
三、
现在貌似没有问题了,但是readfile还是有问题的,虽然PHP的readfile尝试实现的尽量高效,不占用PHP本身的内存,但是实际上它还是需要采用MMAP(如果支持),或者是一个固定的buffer去循环读取文件,直接输出。
那么能不能绕过PHP这层呢,直接由webserver把文件发送给用户呢?可以的,我们可以使用Apache的module mode_xsendfile,让Apache直接发送这个文件给用户。
代码实现如下:(process.php)
header("Content-type: application/octet-stream"); //处理中文文件名 $ua = $_SERVER["HTTP_USER_AGENT"]; $encoded_filename = urlencode($_GET['filename']); $encoded_filename = str_replace("+", "%20", $encoded_filename); if (preg_match("/MSIE/", $ua)) { header('Content-Disposition: attachment; filename="' . $encoded_filename . '"'); } else if (preg_match("/Firefox/", $ua)) { header("Content-Disposition: attachment; filename*=\"utf8''" . $_GET['filename'] . '"'); } else { header('Content-Disposition: attachment; filename="' . $_GET['filename'] . '"'); } //让Xsendfile发送文件 header("X-Sendfile: $_GET['filename']");
最后,如果愿意的话,可以先判断后缀,因为有时候图片当成文件下载也会引起一些不方便的:
$type = strrchr($_GET['filename'], "."); //获取后缀 if($type == "jpg" || "png" || "gif"){ header("Content-Disposition: filename=$_GET['filename']"); //这里我试过,加引号的话,下载时会加到文件名中 header("Content-Type: image/$type"); }
相关文章推荐
- php -- PHP实现点击a标签的href做链接时,直接保存文件(任何类型),而不是通过浏览器直接打开下载的文件
- 文件下载 浏览器直接打开文件而不是保存 相关问题
- .net C# 实现点击下载PDF文件(通知浏览器打开下载对话框) 不是直接打开
- 怎么用<a>标签实现点击链接直接下载文件
- 在文件下载的时候将其保存而不是直接在浏览器上打开!
- a标签的下载链接,如果是图片,点击链接是下载一个图片而不是直接打开,应该怎么办?(添加download属性)
- JavaWeb文件下载,提示用户保存而不是让浏览器直接打开
- android实现通过浏览器点击链接打开apk
- Android实现通过浏览器点击链接打开本地应用(APP)并拿到浏览器传递的数据
- IE中点击链接下载EXCEL文件直接以乱码方式打开解决
- Android实现通过浏览器点击链接打开本地应用(APP)并拿到浏览器传递的数据
- 任何类型的文件下载时都会弹出文件保存框的实现
- 用javascript实现点击链接弹出"图片另存为"而不是直接打开
- 通过PHP实现浏览器点击下载TXT文档(转)
- 任何类型的文件下载时都会弹出文件保存框的实现
- 用javascript实现点击链接弹出"图片另存为"而不是直接打开
- Android实现通过浏览器点击链接打开本地应用(APP)并拿到浏览器传递的数据
- 【其他】Activity中通过 ctrl+鼠标单击 直接链接到对应的xml布局文件,而不是打开R文件。
- Tomcat中点击链接不是下载文件,而是打开文件
- html中用href 实现点击链接弹出文件下载对话框