문제

저는 사이트에서 주로 zip과 mp3를 다운로드하기 위해 강제 다운로드를 사용하고 있습니다(http://pr1pad.kissyour.net) - Google Analytics, 데이터베이스에서 다운로드를 추적하고 실제 다운로드 경로를 숨기려면:

그것은 이것입니다:

extending CI model

... - bunch of code

function _fullread ($sd, $len) {
 $ret = '';
 $read = 0;
 while ($read < $len && ($buf = fread($sd, $len - $read))) {
  $read += strlen($buf);
  $ret .= $buf;
 }
 return $ret;
}

function download(){    
    /* DOWNLOAD ITSELF */

    ini_set('memory_limit', '160M');
    apache_setenv('no-gzip', '1');
    ob_end_flush();

    header("Pragma: public");
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Cache-Control: public",FALSE);
    header("Content-Description: File Transfer");
    header("Content-type: application/octet-stream");
     if (isset($_SERVER['HTTP_USER_AGENT']) && 
      (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false))
      header('Content-Type: application/force-download'); //IE HEADER
    header("Accept-Ranges: bytes");
    header("Content-Disposition: attachment; filename=\"" . basename("dir-with-    files/".$filename) . "\";");
    header("Content-Transfer-Encoding: binary");
    header("Content-Length: " . filesize("dir-with-files/".$filename));

    // Send file for download
    if ($stream = fopen("dir-with-files/$filename", 'rb')){
     while(!feof($stream) && connection_status() == 0){
      //reset time limit for big files
      set_time_limit(0);
      print($this->_fullread($stream,1024*16));
      flush();
     }
     fclose($stream);
    }
}

CI 1.7.2가 포함된 LAMP에 있습니다. 개발 중에 다음과 같은 문제가 발생했기 때문에 인터넷의 다양한 방법을 종합한 나만의 방법입니다.- 서버 제한. ini_set 도움이 되지 않아서 버퍼링을 사용했습니다. _fullread 대신 정상 fread, 대신에 사용되었습니다. @readonly- ob_end_flush(), 사이트가 CI1.7.2에서 수행되었고 버퍼를 정리해야 했기 때문입니다.

지금...작동하지 않습니다.그랬더니 예상 크기/다운로드 시간 표시가 중단되었습니다. 코드를 정리하려고 했는데 코드를 정리하는 동안 문제가 발생했습니다. 무슨 일이 일어났는지 모르겠습니다. 어느 이전 버전 - 작동하지 않았습니다(설정이 전혀 변경되지 않았습니다) - 편집하다:작동하지 않음 = 모든 것을 브라우저 창에 출력합니다.

그래서 내가 말했지, 엿먹어, 여기를 볼게요.

그래서 저는 기본적으로 출력 모델에 넣을 수 있는 스크립트나 함수를 찾습니다.

  • Call Force-Download (Chrome Start 다운로드, 즉, FF, Safari Open the Modal Open/Save/Caint)
  • 파일 크기 및 예상 DL 시간 표시(브라우저에 따라 다르지만 먼저 브라우저가 파일 크기를 알아야 함)
  • PC + Mac(Linux...)의 IE6,7,8, FF3, Opera, Chrome 및 Safari에서 작동합니다(테스트 및 확인됨!).별로 상관 안 함) - 헤더 부분에 대한 것입니다.
  • 서버에는 추가할 수 없는 56MB의 메모리 제한이 있으므로 그것도 중요합니다.

미리 감사드립니다.

편집하다:이제 .htaccess를 사용하여 강제 다운로드를 시도한 이후로 그 어느 때보다 더 망가진 느낌이 듭니다. 작동하는 동안 사소한/중요한 문제가 거의 없었습니다.

  • 그것은 전체 경로를 보여주었습니다 (나에게는 사소한)
  • 전체 다운로드가 완료될 때까지 기다린 다음("연결 중"으로 표시) 다운로드 중임을 표시합니다. 그리고 1초 안에 다운로드됩니다(나에게는 중요).

이제 .htaccess를 삭제했지만 다운로드가 완료될 때까지 기다리고(먼저 캐시에 다운로드하는 것처럼) connected 열기/저장 대화상자를 표시합니다.

도움이 되었습니까?

해결책

그래서이 코드를 사용했습니다 (인터넷에서 찾은 재개 가능한 HTTP 다운로드의 수정 된 버전).

function _output_file($file, $path)
{
    $size = filesize($path.$file);

    @ob_end_clean(); //turn off output buffering to decrease cpu usage

    // required for IE, otherwise Content-Disposition may be ignored
    if(ini_get('zlib.output_compression'))
    ini_set('zlib.output_compression', 'Off');

    header('Content-Type: application/force-download');
    header('Content-Disposition: attachment; filename="'.basename($file).'"');
    header("Content-Transfer-Encoding: binary");
    header('Accept-Ranges: bytes');

    /* The three lines below basically make the 
    download non-cacheable */
    header("Cache-control: no-cache, pre-check=0, post-check=0");
    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); //you may want to change this
    $bytes_send = 0;
    if ($file = fopen($path.$file, 'rb'))
    {
        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('Error - can not open file.');

die();
}

그리고 모델에서 :

function download_file($filename){
    /*
        DOWNLOAD
    */
    $path = "datadirwithmyfiles/"; //directory

    //track analytics

    include('includes/Galvanize.php'); //great plugin
    $GA = new Galvanize('UA-XXXXXXX-7');
    $GA->trackPageView();

    $this->_output_file($filename, $path);

}

Win / Mac의 모든 언급 브라우저에서 예상대로 작동합니다. 지금까지는 문제가 없습니다.

다른 팁

좋아, 이것은 오래된 질문이며 Adam은 이미 자신의 대답을 받아 들였기 때문에 아마도 그는 스스로 일을했지만 왜 그것이 효과가 있었는지 설명하지 못했습니다. 내가 알아 차린 한 가지는 그가 헤더를 사용한 질문이었습니다.

header("Pragma: public");
header("Cache-Control: public",FALSE);

솔루션에서 그는 사용했습니다.

header("Cache-control: private");
header('Pragma: private');

그는 왜 이것을 바꾸 었는지 설명하지 않았지만 SSL의 사용과 관련이 있다고 생각합니다. 최근에 올바른 헤더를 추가하기 위해 다음을 사용하여 HTTP와 HTTPS를 통해 다운로드 할 수있는 소프트웨어에서 유사한 문제를 해결했습니다.

if(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) {
    header("Cache-control: private");
    header('Pragma: private');
} else {
    header('Pragma: public');
}

이 답변의 정보가 위에 유용한 추가 정보를 찾을 수 있기를 바랍니다.

내가 이상한 것을 찾는 한 가지가 있습니다 : 당신은 ob_end_flush() 함수의 시작시. 이것은 실제로 출력 버퍼를 정리하지만 먼저 클라이언트에 모든 것을 출력합니다 (Codeigniter가 설정 한 컨텐츠 헤더를 포함한다고 가정합니다). 전화를 변경하십시오 ob_end_clean(), 그것은 버퍼를 지우고 그것을 폐기합니다. 이렇게하면 자신의 헤더를 생성하기위한 깨끗한 출발을 제공합니다.

또 다른 팁 :

파일을 스트림으로 읽고 블록으로 전달하는 대신이 기능을 시도 할 수 있습니다.

// ...
if (file_exists("dir-with-files/$filename")) {
   readfile($file);
}

이것은 거의 모든 것을 처리합니다.

print($this->_fullread($stream,1024*16));

_fullread가 클래스 내에 있다고 생각합니까? 코드가 위의 코드처럼 보이는 경우 $this-> 작동하지 않을 것입니다.

모든 헤더 항목을 댓글을 달면 파일 내용을 화면에 출력합니까?

어둠 속에서 한 샷 ... 내가 '포스 다운로드'코드 (당신만큼 잘 테스트되지 않은)로 보내는 모든 헤더는 다음과 같습니다. ,거짓);

대신 : 헤더 ( "캐시 제어 : public", false);

그것이 도움이 될지 모르겠습니다.

이런 종류의 "php로 에코" 방법을 수행하려는 경우 남은 시간이나 예상 크기를 사용자에게 표시할 수 없습니다.왜?브라우저가 중간에 다운로드를 재개하려고 하면 PHP에서 해당 사례를 처리할 방법이 없기 때문입니다.

일반적인 파일 다운로드가 있는 경우 Apache는 HTTP를 통해 재개된 다운로드를 지원할 수 있지만 다운로드가 일시 중지된 경우 클라이언트가 다음 청크를 요청할 때 Apache는 스크립트에서 실행 중인 위치를 파악할 방법이 없습니다.

기본적으로 브라우저가 다운로드를 일시 중지하면 웹 서버에 대한 연결이 완전히 종료됩니다.다운로드를 재개하면 연결이 다시 열리고 요청에 "바이트 번호 X에서 시작"이라는 플래그가 포함됩니다.그러나 위의 PHP를 보고 있는 웹서버의 경우 바이트 X는 어디에서 왔습니까?

이론적으로는 다운로드가 중단된 경우 서버가 스크립트를 재개할 위치를 식별하는 것이 가능할 수 있지만 Apache는 재개할 위치를 알아내려고 시도하지 않습니다.결과적으로, 브라우저로 전송된 헤더에는 서버가 이력서를 지원하지 않는다는 내용이 명시되어 있으며, 이로 인해 대부분의 주요 브라우저에서 예상되는 파일 크기 및 시간 제한 항목이 꺼집니다.

편집하다:이 경우를 처리할 수 있을 것 같지만 많은 코드가 필요합니다.보다 http://www.php.net/manual/en/function.fread.php#84115 .

세계에서 다운로드 경로를 숨기려고 시도하는 대신 외부에서 액세스 할 수 없게 만들고 위의 스크립트로 파일에만 액세스 할 수 있습니다. 그렇게하려면 디렉토리에 htaccess 파일 ( '.htaccess'라는 이름의 문자 파일)을 넣습니다. htaccess의 내용은 다음과 같습니다.

order deny,allow
deny from all
allow from localhost

이제 *World의 경로에 액세스하려고하면 웹 서버가 401 금식을 만들어냅니다.

모호함을 통한 보안은 당신이 원하는 것이 아닙니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top