2014-02-24 9 views
0

readfile()을 통해 큰 파일을 보내려고합니다.Readfile은 큰 파일에서 0 바이트를 읽습니까?

그러나, 아무것도 브라우저와 readfile() 반환 0로 전송되지 않습니다 (하지false!).

내가 보내려고하는 파일 크기는 4GiB이며 PHP에서 읽을 수 있습니다.
길이가 긴 다운로드 프로세스를 허용하도록 set_time_limit(0)을 설정합니다.

012K(4K 청크) 및 echo (으)로 while 루프를 사용하여 파일을 보내려고했으나 500-2500 MiB가 다운로드 된 후 임의로 오류없이 중단되고 다운로드를 완료하지 못합니다.

코드

$f = '/var/www/tmp/largefile.dat'; 

var_dump(file_exists($f)); 
var_dump(is_readable($f)); 
var_dump(filesize($f)); 

$rf = readfile($f); 
var_dump($rf); 

다음 출력 생성 다음 시험 (0)

testfile 위

불리언 (TRUE) 불리언 (TRUE) INT (4294967296) 지능 다음 명령을 사용하여 만들었습니다.

dd if=/dev/zero of=largefile.dat bs=1M count=4096 

내가 뭘 잘못하고 어떻게 해결합니까? 새로운 아파치 버전


편집 2014-07
업그레이드 지금이 문제를 해결했다.

+0

질문에 대답하지 않지만 큰 파일을 제공합니다. 아마도 https://tn123.org/mod_xsendfile/을 사용하여 웹 서버에 이메일을 남기는 것이 좋습니다. – deceze

+0

@deceze 당신의 해답 **는 ** 질문에 대답합니다 (그리고 당신은 그런 식으로 공식화해야합니다)! 'mod-xsendfile'을 사용하면 Apache의 내부 구조를 사용하고 PHP 기반 솔루션보다 훨씬 뛰어난 안정성과 성능을 제공하므로 아래에 언급 한 답변보다 훨씬 낫습니다. 아파치 서버에서 스크립트가 실행되지 않거나'mod-xsendfile'을 사용하는 이유가 옵션이 아닌 경우에만 대답을 고려해야합니다. – trejder

답변

2

PHP readfile은 보내기 전에 페이지 버퍼를 사용하여 파일을 저장합니다. 메모리 부족으로 인해 실패하면 메모리 오류가 발생하지 않습니다.

큰 파일은 파일을 읽고 내용을 넣어하는 FREAD fopen을 사용하는 다운로드가 페이지 버퍼 ( http://us1.php.net/manual/es/ref.outcontrol.php)

를 사용하지 않는 수행하는 페이지가 또한 더 있는지 확인합니다. 또한 fread를 사용하면 다운로드를 다시 시작할 수 있도록 몇 가지 코드를 만들 수 있습니다.0, Resumable downloads when using PHP to send the file?


프로세스 (위해서는 ob_end_clean 시작하기 전에 청소를 시도해야 OUPUT 버퍼링과 끝 버퍼를 해제) 또는 ('output_buffering을'을 위해서는 ini_set 사용하여 출력 버퍼링을 비활성화하려면 :

여기 좋은 예가있다); 또한 같은 페이지에서 부분적인 다운로드를 지원하는 예를 들어 거기

http://ca2.php.net/manual/en/function.readfile.php#48683

function readfile_chunked($filename,$retbytes=true) { 
    $chunksize = 1*(1024*1024); // how many bytes per chunk 
    $buffer = ''; 
    $cnt =0; 
    $handle = fopen($filename, 'rb'); 
    if ($handle === false) { 
     return false; 
    } 
    while (!feof($handle)) { 
     $buffer = fread($handle, $chunksize); 
     echo $buffer; 
     ob_flush(); 
     flush(); 
     if ($retbytes) { 
      $cnt += strlen($buffer); 
     } 
    } 
     $status = fclose($handle); 
    if ($retbytes && $status) { 
     return $cnt; // return num. bytes delivered like readfile() does. 
    } 
    return $status; 

} 

을 다음 ReadFile을 문서에


대신 ReadFile을 긴 파일 FREAD를 사용하는 방법에 대한 예를 들어있어 :

function smartReadFile($location, $filename, $mimeType='application/octet-stream') { 
    if(!file_exists($location)) { 
    header ("HTTP/1.0 404 Not Found"); 
    return; 
    } 

    $size=filesize($location); 
    $time=date('r',filemtime($location)); 

    [email protected]($location,'rb'); 
    if(!$fm) { 
    header ("HTTP/1.0 505 Internal server error"); 
    return; 
    } 

    $begin=0; 
    $end=$size; 

    if(isset($_SERVER['HTTP_RANGE'])) { 
    if(preg_match('/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches)){ 
     $begin=intval($matches[0]); 
     if(!empty($matches[1])){ $end=intval($matches[1]); } 
    } 
    } 

    if($begin>0||$end<$size){ 
    header('HTTP/1.0 206 Partial Content'); 
    }else{ 
    header('HTTP/1.0 200 OK'); 
    } 

    header("Content-Type: $mimeType"); 
    header('Cache-Control: public, must-revalidate, max-age=0'); 
    header('Pragma: no-cache'); 
    header('Accept-Ranges: bytes'); 
    header('Content-Length:'.($end-$begin)); 
    header("Content-Range: bytes $begin-$end/$size"); 
    header("Content-Disposition: inline; filename=$filename"); 
    header("Content-Transfer-Encoding: binary\n"); 
    header("Last-Modified: $time"); 
    header('Connection: close'); 

    $cur=$begin; 
    fseek($fm,$begin,0); 

    while(!feof($fm)&&$cur<$end&&(connection_status()==0)) { 
    print fread($fm,min(1024*16,$end-$cur)); 
    $cur+=1024*16; 
    } 
} 
+0

'fread()'메서드에 대한 재개 가능한 다운로드를 구현했지만 여전히 무작위로 중단됩니다. 또한, 루프 이전에'ob_end_clean()'을하거나'readfile()'을 쓰지 않아도된다. –