PHP - проблема с потоковой передачей файлов - PullRequest
2 голосов
/ 30 декабря 2010

У меня есть веб-сайт с разделом загрузок. Мне нужно, чтобы файлы не были доступны напрямую анонимному пользователю, поэтому я помещаю их в каталог, недоступный для пользователей, но доступный для веб-сервера. Когда пользователь щелкает ссылку для загрузки файла, мне нужно перенаправить его на страницу загрузки, на которой файл будет передан пользователю без его знания местоположения каталога или файла и попросит его сохранить его на своем компьютере , Я нашел следующий код в предыдущем посте, но не могу заставить его работать правильно. Может быть, я не знаю правильных имен переменных, которые он хочет передать ему. Пожалуйста, включите объяснение того, как использовать ваш код.

$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);

Ответы [ 3 ]

1 голос
/ 31 декабря 2010

Вот как я бы это сделал.

<?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, чтобы пользовательможет иметь представление о том, сколько времени это займет.Или вы можете жестко закодировать этот номер, если вы всегда будете подавать один и тот же файл.

0 голосов
/ 30 декабря 2010

Вот функция, которую я (видимо, другие? !!?) Разработал, чтобы сделать это, это мясистая функция, но она проверяет и делает все.Чтобы назвать это просто, хотя, важная часть.У меня это внутри класса, но вы можете просто сделать его функцией 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);
0 голосов
/ 30 декабря 2010

Допустим, у вас есть файл в каталоге с именем 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.

...