之前做项目遇到这样一个问题,就是在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. Dynamic Proxy (动态代理模式)

    动态代理(运行期行为)主要有一个 Proxy类 和一个 InvocationHandler接口 动态代理角色: 1. 抽象主题角色 2. 真实主题角色(实现了抽象主题接口) 3. 动态代理主题角色(实 ...

  2. UML回想-通信图

        我们对软件project这一大块的学习事实上開始的还是挺早的,而且在后来的学习过程中也不断的涉及到了这些知识. 可是,经过软考的检验来看我对软工这一块的内容掌握的实在是慘不忍睹.基本上就是一出 ...

  3. 在ubuntu下如何搜索文件?

    来自 1.whereis 文件名 特点:快速,但是是模糊查找,例如 找 #whereis mysql 它会把mysql,mysql.ini,mysql.*所在的目录都找出来.我一般的查找都用这条命令. ...

  4. Kubuntu 初始配置

    1.中文配置 系统设置中地区选择中国.语系使用简体中文(一開始可能仅仅有英文选项,似乎是更新后能够选择其它语言了) 安装中文字体: sudo apt-get install ttf-wqy-micro ...

  5. 【php】php路径目录解析函数dirname basename pathinfo区别及实例

    php获取路径.目录或文件名称,我们经常会使用到dirname().basename().pathinfo()这三个函数,本文章向大家详细介绍这三个函数的区别以及使用实例,需要的朋友可以参考一下. d ...

  6. Linux-ssh证书登录(实例详解)

    前言 本文基于实际Linux管理工作,实例讲解工作中使用ssh证书登录的实际流程,讲解ssh证书登录的配置原理,基于配置原理,解决实际工作中,windows下使用SecureCRT证书登录的各种问题, ...

  7. MS SQL自定义函数IsPositiveInteger MS SQL自定义函数IsNumeric 水晶报表使用IEnumerable<T>数据源

    MS SQL自定义函数IsPositiveInteger   判断字符串是否为正整数,0开始的的数字不算. SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON ...

  8. js 温故而知新 用typeof 来判断一个未定义的变量

    一直以为,如果你使用一个未定义的变量,肯定会报错.甚至根本不可能有这种场景. 但仔细想想还是有的,譬如你要判断全局是否存在$变量.或者要为全局暴漏一个全局变量之前,先判断是否有这个变量. typeof ...

  9. java中long型时间戳的计算

    计算时间的时候碰到的问题: Date d = new Date(); long currtime = d.getTime(); //获取当前时间 long starttime = currtime - ...

  10. sysbench的安装详解

    sysbench是一个压力测试工具.可以用它来测试cpu.mem.disk.thread.mysql.postgr.oracle:然而作为一个mysql dba 我当然是用它来压测mysql啦! 一. ...