2014-04-08 15 views
8

Utilizzo uno script PHP per controllare l'accesso ai file di download. Funziona bene per qualsiasi cosa con meno di 2 GB, ma non riesce per i file più grandi.Impossibile scaricare file di grandi dimensioni Apache/PHP (> 2 Gb)

  • Apache e PHP sono entrambi a 64 bit
  • Apache sarà permettere al file da scaricare se si accede direttamente (che io non posso permettere)

Le viscere della PHP (ignorando il il controllo di accesso):

if (ob_get_level()) ob_end_clean(); 

error_log('FILETEST: '.$path.' : '.filesize($path)); 
header('Content-Description: File Transfer'); 
header('Content-Type: application/octet-stream'); 
header('Content-Disposition: attachment; filename='.basename($path)); 
header('Expires: 0'); 
header('Cache-Control: must-revalidate'); 
header('Pragma: public'); 
header('Content-Length: ' . filesize($path)); 
readfile($path); 
exit; 

il log di errore mostra la dimensione del file multa

0.123.516,41 mila
[Tue Apr 08 11:01:16 2014] [error] [client *.*.*.*] FILETEST: /downloads/file.name : 2251373807, referer: http://myurl/files/ 

Ma il log di accesso ha una dimensione negativa:

*.*.*.* - - [08/Apr/2014:11:01:16 +0100] "GET /files/file.name HTTP/1.1" 200 -2043593489 "http://myurl/files/" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0" 

E così i browser si rifiutano di scaricare il file. Infatti, utilizzando wget, non è l'invio di nulla:

$ wget -S -O - http://myurl/files/file.name 
--2014-04-08 11:33:38-- http://myurl/files/file.name 
HTTP request sent, awaiting response... No data received. 
Retrying. 
+1

Questo è in genere dovuto all'ampio consumo di memoria, utilizzare fread per mantenere il flusso di grandi dimensioni invece su 2 GB in una volta. –

+0

Si trova sul tuo computer di hosting o sul PC di casa? Qual è il file system e il sistema operativo sottostanti? –

+0

Proverei davvero il suggerimento di Jason. Immagina 8 persone che richiedono 2 GB in una volta = 16 GB di memoria ..usare 'fread' (e' fopen' con 'b' flag) ti permetterà anche di controllare la velocità massima di un download. – DanFromGermany

risposta

6

provare a leggere il file in blocchi ed esporli al browser invece di riempire la memoria locale con 2 GB e vampate di calore tutto in una volta.

Sostituire readfile($path); da:

@ob_end_flush(); 
flush(); 

$fileDescriptor = fopen($file, 'rb'); 

while ($chunk = fread($fileDescriptor, 8192)) { 
    echo $chunk; 
    @ob_end_flush(); 
    flush(); 
} 

fclose($fileDescriptor); 
exit; 

8192 byte è un punto critico in alcuni casi, a RIFERIME php.net/fread.

L'aggiunta di alcune variabili microtime (e il confronto con la posizione del puntatore del descrittore di file) consentirà inoltre di controllare la velocità massima del download.

* (Flushing anche un po 'il buffer di uscita dipende dal server web, utilizzare i comandi per essere sicuri che almeno cerca di svuotare il più possibile.)

+2

Inizio molto più veloce per il download usando questo piuttosto che un file readfile. Stavo lottando con questo per un po ': ti devo una birra ...! – BSUK

0

mi sono imbattuto in questo problema prima e usato lo script di seguito per scaricare file, si rompe il file in blocchi per scaricare file di grandi dimensioni invece di cercare di prendere tutta la file in una volta. Questo script tiene anche conto del browser utilizzato come alcuni browser (ovvero IE) in grado di gestire le intestazioni in modo leggermente diverso.

private function outputFile($file, $name, $mime_type='') { 
    $fileChunkSize = 1024*30; 

    if(!is_readable($file)) die('File not found or inaccessible!'); 

    $size = filesize($file); 
    $name = rawurldecode($name); 

    $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($file,"."),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(); 

    if(ini_get('zlib.output_compression')) 
     ini_set('zlib.output_compression', 'Off'); 

    header('Content-Type: ' . $mime_type); 
    header('Content-Disposition: attachment; filename="'.$name.'"'); 
    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"); 

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

    $chunksize = 1*($fileChunkSize); 
    $bytes_send = 0; 
    if ($file = fopen($file, 'r')) 
    { 
     if(isset($_SERVER['HTTP_RANGE'])) 
     fseek($file, $range); 

     while(!feof($file) && 
      (!connection_aborted()) && 
      ($bytes_send<$new_length) 
     ) 
     { 
      $buffer = fread($file, $chunksize); 
      print($buffer); 
      flush(); 
      $bytes_send += strlen($buffer); 
     } 
    fclose($file); 
    } 
    else die('Error - can not open file.'); 

    die(); 
} 
0

Aggiungere il codice prima di readfile (percorso $);

ob_clean(); 
flush(); 

Io uso questo codice per il download:

if (file_exists($file)) { 


     header('Content-Description: File Transfer'); 
     header('Content-Type: application/octet-stream'); 
     header('Content-Disposition: attachment; filename='.basename($file)); 
     header('Content-Transfer-Encoding: binary'); 
     header('Expires: 0'); 
     header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); 
     header('Pragma: public'); 
     header('Content-Length: ' . filesize($file)); 

     ob_clean(); 
     flush(); 
     readfile($file); 
     exit; 

    } 
0

La vostra scelta migliore è quello di costringere apache a http modalità Chunked con una funzione come questa. In questo modo risparmierai molta memoria PHP.

function readfile_chunked($filename, $retbytes = TRUE) { 
    $CHUNK_SIZE=1024*1024; 
    $buffer = ''; 
    $cnt =0; 
    $handle = fopen($filename, 'rb'); 
    if ($handle === false) { 
    return false; 
    } 
    while (!feof($handle)) { 
    $buffer = fread($handle, $CHUNK_SIZE); 
    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; 
} 
Problemi correlati