zoukankan      html  css  js  c++  java
  • php -- PHP实现点击a标签的href做链接时,直接保存文件(任何类型),而不是通过浏览器直接打开下载的文件

    之前做项目遇到这样一个问题,就是在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

  • 相关阅读:
    VS2008编写MFC程序--使用opencv2.4()
    November 02nd, 2017 Week 44th Thursday
    November 01st, 2017 Week 44th Wednesday
    October 31st, 2017 Week 44th Tuesday
    October 30th, 2017 Week 44th Monday
    October 29th, 2017 Week 44th Sunday
    October 28th, 2017 Week 43rd Saturday
    October 27th, 2017 Week 43rd Friday
    October 26th, 2017 Week 43rd Thursday
    October 25th, 2017 Week 43rd Wednesday
  • 原文地址:https://www.cnblogs.com/hf8051/p/4651906.html
Copyright © 2011-2022 走看看