之前做项目遇到这样一个问题,就是在php环境下,用a标签的href链接到一个资源,比如是mp3或者lrc文件时,点击之后不是出现保存文件的提示,而是调用本地程序打开文件或者直接在浏览器上解析。网上说可以全部做成rar格式的文件,这个一方面不方便,有些情况下也不可能完全这样做,还有实际上,做过测试会发现,在content-type:text/html的情况下,即时是rar有时也会被浏览器直接解析,无法实现下载的功能,那这个问题是不是就无解了呢?答案是否定的,几番搜索+测试,终于发现了一个可行的解决方案,就是在点击a标签的链接之后,不是直接请求资源,而是对header做一下预处理,再去readfile就可以了。下面附上相关的测试代码:

一、

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");
}

转自:http://m.blog.csdn.net/blog/nkliming/8536311

php -- PHP实现点击a标签的href做链接时,直接保存文件(任何类型),而不是通过浏览器直接打开下载的文件的更多相关文章

  1. 微信下载app需要点击右上角在浏览器中打开下载的问题

    很多朋友是不是遇到过这样的问题,自家的app通过微信推广没办法直接下载,而是需要通过一个遮罩层来提示用户下载. 点击下载按钮提示点击右上角在浏览器中打开 这样的方式下载一个app是不是需要点击下载按钮 ...

  2. 阻止点击<a>标签链接跳转

      我们常用的在a标签中有点击事件(<a href="地址">链接</a>),其中“href”参数只要不为空,点击该链接时,页面会自动跳转:如果指定的“hr ...

  3. 移动端页面点击a标签会有半透明的阴影或红色边框的bug

    好久没有更新了,今天来一发 ^_^ 最近在写移动端页面,测试时发现一个a标签的bug:无论是iOS端还是Android端都存在,当点击a标签,会有一个矩形的透明的阴影闪一下(不同的浏览器阴影的颜色还不 ...

  4. 点击a标签,跳转到iframe中,并在iframe中显示指定的页面

    点击a标签,跳转到iframe中,并在iframe中显示指定的页面 1.用a标签的target属性 <iframe id="myFrameId" name="myF ...

  5. 鼠标模拟点击a标签

    今天写程序遇到的,想要用鼠标模拟点击a标签 html代码如下: <a id="jump"></a> js代码如下: var page = ....; $(' ...

  6. ie8下jquery读取当前点击的标签位置错误,原因是里面有内容写了text-indent:-9999px

    今天写一地图的效果,鼠标点击对应的区域,弹出所点击区域的名字. 因为设计的区域名字有特殊效果,所以,在点击区域里面套了个标签写上区域名字用来识别,但是这个文字呢不同显示在页面上,所以就给 em 加个了 ...

  7. 对Ul下的li标签执行点击事件——如何获取你所点击的标签

    问题所来:做项目时,一般的数据都是用循环动态加载出来的,结构都是一样的,只是绑定的值不同,如何对相同的标签做处理的问题就来了. 例如:点谁就显示谁的数值 <ul > <li id=& ...

  8. 使用JS或jQuery模拟鼠标点击a标签事件代码

    原文 使用JS或jQuery模拟鼠标点击a标签事件代码 这篇文章主要介绍了使用JS或jQuery模拟鼠标点击a标签事件代码,需要的朋友可以参考下 <a id="alink" ...

  9. 微信浏览器返回刷新,监听微信浏览器返回事件,网页防复制,移动端禁止图片长按和vivo手机点击img标签放大图片

    以下代码都经过iphone7,华为MT7 ,谷歌浏览器,微信开发者工具,PC端微信验证.如有bug,还请在评论区留言. demo链接:https://pan.baidu.com/s/1c35mbjM ...

随机推荐

  1. 算法笔记_153:算法提高 判断名次(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 某场比赛过后,你想要知道A~E五个人的排名是什么,于是要求他们每个人说了一句话.(经典的开头……-_-!)得了第1名的人23,说了假话:得 ...

  2. 用oracle建表,必须注意Oracle 关键字(保留字)

    Oracle 关键字(保留字) 大全   转 其实这个东西可以在oracle 上输入一个sql语句就可以得到: select * from v$reserved_words order by keyw ...

  3. POJ 3468 A Simple Problem with Integers 【线段树,区间更新】

    题意:你有N个整数,A1,A2,-,一个.你须要处理两种类型的操作.一种类型的操作是加入了一些给定的数字,每一个数字在一个给定的时间间隔. 还有一种是在给定的时间间隔要求数量的总和. 难点:主要是la ...

  4. Solr之困

    http://www.kafka0102.com/2010/08/319.html重写公司的站内搜索.经过前期一段时间对lucene和solr的熟悉,最后决定使用Solr作为新系统的基础框架.现在已经 ...

  5. Java compiler level does not match the version of the installed Java project fac

    Java compiler level does not match the version of the installed Java project fac 问题一: 问:项目图标报错,Probl ...

  6. [svc]linux测速软件speedtest-cli

    linux测速软件: https://github.com/sivel/speedtest-cli 以下是我在gentoo上测的

  7. linux命令汇总1

    允许非root用户使用“sudo” root身份登录系统,执行“visudo”,根据示例添加新的一个规则(记住输入的密码是当前用户密码,而不是root密码)#不需要密码执行sudo命令hadoop   ...

  8. JAVA多线程之synchronized和volatile实例讲解

    在多线程中,提到线程安全.线程同步,我们经常会想到两个关键字:volatile和synchronized,那么这两者有什么区别呢? 1. volatile修饰的变量具有可见性 volatile是变量修 ...

  9. iOS_文件上传进度条的实现思路-AFNettworking

    iOS_文件上传进度条的实现思路-AFNettworking //要上传的文件名,在这里我使用当前日期做为文件的名称 NSString * fileName =[NSString stringWith ...

  10. C++之string的底层真的是用char数组来实现的么?

    一.引言 遇到一个问题:使用加密库对数据进行加密,得到密文,使用string进行保存并传输,然后可以正确解密出来,但是使用string.c_str()进行参数传递则无法正确解密出明文. 原因是:密文中 ...