【问题标题】:PHP - File Streaming problemPHP - 文件流问题
【发布时间】:2010-12-30 20:22:12
【问题描述】:

我有一个有下载部分的网站。我需要匿名用户不能直接访问这些文件,因此我将它们放在用户无法访问但 Web 服务器可以访问的目录中。当用户单击链接下载文件时,我需要将其重定向到下载页面,该页面会将文件流式传输给用户,而他不知道目录或文件的位置,并且会要求他将其保存到他的计算机.我在上一篇文章中找到了以下代码,但我无法让它正常工作。可能是我不知道要传递给它的变量的正确名称。请说明如何使用您的代码。

$filename='Firefox%20Setup%203.6.13.exe';
$file_path='http://ftp.byfly.by/pub/mozilla.org/firefox/releases/3.6.13/win32/fr';
$file= $file_path."/".$filename;
$len=filesize($file);
header("content-type: application/save");
header("content-length: $len");
header("content-disposition: attachment; filename=$filename");
$fp=fopen($file, "r");
fpassthru($fp);

【问题讨论】:

    标签: php streaming stream


    【解决方案1】:

    我会这样做。

    <?php
    
    function getFile($file_location) {
        header('Content-Description: File Transfer');
        header('Content-type: application/exe');
        header('Content-Disposition: attachment; filename="supercoolFF.exe"');
        header('Content-Transfer-Encoding: binary');
        ob_end_clean();
        $url_info = parse_url($file_location);
        if (!isset($url_info['query'])) $url_info['query'] = '';
        $http = fsockopen($url_info['host'],$url_info['port']);
        $req = "GET " . $url_info['path'] . "?" . $url_info['query'] . " HTTP/1.1\r\n";
        $req .= "Host: " . $url_info['host'] . ":" . $url_info['port'] . "\r\n";
        $req .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n";
        $req .= "User-Agent  Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8\r\n";
        $req .= "Accept-Language: en-us,en;q=0.5\r\n";
        $req .= "Accept-Encoding: gzip,deflate\r\n";
        $req .= "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n";
        if ($len = strlen($url_info['query']) {
            $req .= 'Content-Length: ' . $len . "\r\n";
            $req .= "Connection: Close\r\n\r\n";
            $req .= $query . "\r\n\r\n";
        } else {
            $req .= "Connection: Keep-Alive\r\n\r\n";
        }
        fputs($http, $req);
    
        $content = "";
        $content_encountered = FALSE;
        ob_end_clean();
        while(strlen($part = fgets($http, 4096))) {
            if ($content_encountered) {
                echo $part;
                $content .= $part; 
            }
            if ($part == "\r\n") {
                $content_encountered = TRUE;
            }
        }
        fclose($http);
        exit;
    }
    
    $filename='Firefox%20Setup%203.6.13.exe?';
    $file_path='http://ftp.byfly.by:80/pub/mozilla.org/firefox/releases/3.6.13/win32/fr';
    
    getFile($file_path . '/' . $filename);
    

    当然,最好先执行 HEAD 请求以获取文件大小并在响应中包含 Content-Length 标头,以便用户了解需要多长时间。或者,如果您总是要提供同一个文件,您可以硬编码该数字。

    【讨论】:

    • 效果很好,正如你所说,如果有办法知道文件大小会更好。我有一个问题,我该如何使用这个脚本并使下载恢复工作
    【解决方案2】:

    假设您在名为“hiddenaccess”的目录中有一个文件,文件名为“test.mp3”,您可以在php变量中加载路径并让它下载

    <?php
    $file = "./hiddenaccess/test.mp3";
    header("Content-Disposition: attachment; filename=" . urlencode($file));
    header("Content-Type: application/force-download");
    header("Content-Type: application/octet-stream");
    header("Content-Type: application/download");
    header("Content-Description: File Transfer");             
    header("Content-Length: " . filesize($file));
    
    @readfile($file);
    
    ?>
    

    注意:此文件中不要有任何其他 echo 或 print 语句。

    【讨论】:

    • 你应该在readfile之前调用session_write_close(),这样用户可以在下载文件的同时浏览页面。
    • 当我下载文件时只有 305 个八位字节
    • 对不起,为此,我一直在第一个示例(Firefox 链接)上进行测试!它有效,但仅在同一服务器上。如果它是外部服务器,我怎样才能使它工作?
    【解决方案3】:

    这是我(显然是其他人?!!?)为此开发的一个函数,它是一个丰富的函数,但它会检查并执行所有操作。调用它很简单,但重要的部分。我在一个类中有这个,但你可以让它成为一个 php 函数,因为它不依赖于其他类函数。希望对您有所帮助。

       public static function output_file($path, $filename, $mime_type='') {
    
        $err = 'Sorry, the file you are requesting is unavailable.';
    
        $filename = rawurldecode($filename);
    
        // check that file exists and is readable
        if (file_exists($path) && is_readable($path)) {
    
            /* Figure out the MIME type (if not specified) */
            $known_mime_types=array(
             "pdf" => "application/pdf",
             "txt" => "text/plain",
             "html" => "text/html",
             "htm" => "text/html",
             "exe" => "application/octet-stream",
             "zip" => "application/zip",
             "doc" => "application/msword",
             "xls" => "application/vnd.ms-excel",
             "ppt" => "application/vnd.ms-powerpoint",
             "gif" => "image/gif",
             "png" => "image/png",
             "jpeg"=> "image/jpg",
             "jpg" =>  "image/jpg",
             "php" => "text/plain"
            );
    
            if($mime_type==''){
                $file_extension = strtolower(substr(strrchr($filename,"."),1));
                if(array_key_exists($file_extension, $known_mime_types)){
                 $mime_type=$known_mime_types[$file_extension];
                } else {
                 $mime_type="application/force-download";
                };
            };
    
            @ob_end_clean(); //turn off output buffering to decrease cpu usage
    
            if(ini_get('zlib.output_compression')) { //otherwise the filesize is way off
                ini_set('zlib.output_compression', 'Off');
            }
    
            // get the file size and send the http headers
            $size = filesize($path);
            header("Content-Type: $mime_type");
            header('Content-Disposition: attachment; filename="'.$filename.'"');
            header('Content-Transfer-Encoding: binary');
            header('Accept-Ranges: bytes');
            header('Cache-control: private');
            header('Pragma: private');
            header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
    
            // multipart-download and download resuming support
            if(isset($_SERVER['HTTP_RANGE'])) {
    
                list($a, $range) = explode("=", $_SERVER['HTTP_RANGE'], 2);
                list($range) = explode(",", $range, 2);
                list($range, $range_end) = explode("-", $range);
    
                $range=intval($range);
    
                if(!$range_end) {
                    $range_end = $size-1;
                } else {
                    $range_end = intval($range_end);
                }
    
                $new_length = $range_end-$range+1;
    
                header("HTTP/1.1 206 Partial Content");
                header("Content-Length: $new_length");
                header("Content-Range: bytes $range-$range_end/$size");
            } else {
                $new_length = $size;
                header("Content-Length: " . $size);
            }
    
            /* output the file itself */
            $chunksize = 1*(1024*1024); //may want to change this
            $bytes_send = 0;
    
            if ($file = fopen($path, 'r')) {
                if(isset($_SERVER['HTTP_RANGE'])) {
                    fseek($file, $range);
                }
    
                while(!feof($file) && (!connection_aborted()) && ($bytes_send<$new_length)) {
                    $buffer = fread($file, $chunksize);
                    print($buffer); //echo($buffer); // is also possible
                    flush();
                    $bytes_send += strlen($buffer);
                }
    
                fclose($file);
    
            } else {
                die($err);
            }
    
        } else {
    
            die($err);
    
        }
    
        die();
    }
    

    使用它会是这样的。

    $path = '/var/www/site/httpdocs/upload/private/myfile.txt';
    $dl_filename = 'NDA_'.time();
    $mime_type = 'doc';
    output_file($path,$dl_filename,$mime_type);
    

    【讨论】:

    • 非常感谢您的回答!
    • 但是你能告诉我我应该在 $dl_filename 中放什么吗,正如我在 $path 中看到的那样,你把两者都放了(路径和文件名)
    • -1。把别人的工作归功于别人是一个禁忌。 stackoverflow.com/questions/386845/…
    • $dl_filename 只是您希望用户下载文件的字符串。所以“my_game”它会为你添加扩展。 @sberry2A 这已经在我们的库中多年了,因为你可以看到它是旧的 php3 兼容代码,我们继续使用和塑造它,据我所知,他们从我们这里拿走了它。我不知道这是一个如此受欢迎的功能,我得看看这个。
    • 我比你 sberry2A 的开发时间长,我比你小 7 岁,这告诉你什么?它不可能是我的代码,只是因为站点 A 有它。好吧,我在这里要说的是,我是在 5 年前从头开始做的,我们继续在希望下载安全性的小型客户上使用它。它并不完美,有更好的但低流量的客户不需要更好,他们需要工作。继续降级我,上次我在这个网站上提供帮助。
    猜你喜欢
    • 2011-04-28
    • 2012-09-29
    • 1970-01-01
    • 2020-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-07
    相关资源
    最近更新 更多