2011-06-29 25 views
26

Utilizando PHP, estoy tratando de servir archivos de gran tamaño (hasta posiblemente 200 MB) que no están en un directorio accesible a través de la web debido a problemas de autorización. Actualmente, utilizo una llamada readfile() junto con algunos encabezados para servir el archivo, pero parece que PHP lo está cargando en la memoria antes de enviarlo. Tengo la intención de implementar en un servidor de alojamiento compartido, lo que no me permitirá usar mucha memoria o agregar mis propios módulos de Apache como X-Sendfile.Cómo descargar archivos de gran tamaño a través de la secuencia de comandos PHP

No puedo permitir que mis archivos estén en un directorio accesible por razones de seguridad. ¿Alguien sabe un método que consume menos memoria y que puedo implementar en un servidor de alojamiento compartido?

EDIT:

if(/* My authorization here */) { 
     $path = "/uploads/"; 
     $name = $row[0];   //This is a MySQL reference with the filename 
     $fullname = $path . $name; //Create filename 
     $fd = fopen($fullname, "rb"); 
     if ($fd) { 
      $fsize = filesize($fullname); 
      $path_parts = pathinfo($fullname); 
      $ext = strtolower($path_parts["extension"]); 
      switch ($ext) { 
       case "pdf": 
       header("Content-type: application/pdf"); 
       break; 
       case "zip": 
       header("Content-type: application/zip"); 
       break; 
       default: 
       header("Content-type: application/octet-stream"); 
       break; 
      } 
      header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\""); 
      header("Content-length: $fsize"); 
      header("Cache-control: private"); //use this to open files directly 
      while(!feof($fd)) { 
       $buffer = fread($fd, 1*(1024*1024)); 
       echo $buffer; 
       ob_flush(); 
       flush(); //These two flush commands seem to have helped with performance 
      } 
     } 
     else { 
      echo "Error opening file"; 
     } 
     fclose($fd); 
+4

No estoy de acuerdo con esto es un duplicado, este tipo está tratando de descargar a través de PHP, donde-como la otra pregunta es cómo descargar un archivo grande ... diferentes problemas con sinceridad. –

Respuesta

10

Si utiliza fopen y fread en lugar de readfile, que debe resolver su problema.

Hay a solution en la documentación de readfile de PHP que muestra cómo usar fread para hacer lo que quiera.

+0

El uso de un archivo de lectura fragmentado con 'flush()' y 'ob_flush()' funciona como se indica en la documentación. Ahora estoy teniendo problemas con la descarga que acaba de ser interrumpida cerca del final del archivo. ¿Alguna idea allí? – brenns10

+0

@ brenns10 - El ejemplo no usa 'flush()' y 'ob_flush()'. Usarlos hará que los datos se guarden en la memoria hasta que se muestren, algo que es posible que desee evitar si tiene recursos limitados. Dicho esto, no estoy seguro de por qué se salta el final sin ver tu código.Podría ser que los últimos pocos bytes del archivo no se leen o que no esté sonrojando después de la última lectura. –

+0

He intentado usar el script tanto con los comandos de vaciado como sin él, y cuando los uso, la descarga comienza inmediatamente y no usa mucha memoria (en mi WAMP). Cuando no uso los comandos de descarga, hay un gran retraso antes de que comience la descarga (que, supongo, es PHP leyendo el archivo en la memoria), y durante ese tiempo hay un gran aumento de RAM. Publicaré el código que estoy usando en la pregunta original. – brenns10

2

También podría manejar esto en el estilo del Nudo Gordiano, es decir, evitar el problema por completo. Guarde los archivos en un directorio no accesible, y cuando se inicia una descarga puede simplemente

tempstring = rand(); 
symlink('/filestore/filename.extension', '/www/downloads'.tempstring.'-filename.extension'); 
echo("Your download is available here: <a href='/downloads/'.tempstring.'-filename.extension'); 

y la configuración de una tarea programada a unlink() cualquier enlaces de descarga de más de 10 minutos. Prácticamente no se requiere el procesamiento de sus datos, no hay que dar masajes a los encabezados HTTP, etc.

Incluso hay un par de bibliotecas para este propósito.

3

Si le importa el rendimiento, hay xsendfile, disponible en apache, nginx y lighttpd como módulo. Verifique los comentarios de los usuarios del readfile() doc.

También hay módulos para estos servidores web que aceptan una url con un valor hash adicional que permite descargar el archivo durante un breve período de tiempo. Esto también se puede usar para resolver problemas de autorización.

+0

Esto suena como una alternativa útil, pero ¿es esto algo que puedo hacer en un servidor de alojamiento compartido? Creo que las empresas de hosting tienen algunos módulos configurados que usan, por lo que probablemente no pueda usar esto en un servidor compartido. – brenns10

+0

Bien, ya has respondido tu pregunta :) Pregúntales ... –

4

Para descargar archivos de gran tamaño desde un servidor, he cambiado la siguiente configuración en el archivo php.ini:

Upload_max_filesize - 1500 M 
Max_input_time - 1000 
Memory_limit - 640M 
Max_execution_time - 1800 
Post_max_size - 2000 M 

Ahora, yo soy capaz de cargar y descargar vídeo de 175 MB en el servidor. Desde entonces, tengo el servidor dedicado. Entonces, hacer estos cambios fue fácil.

A continuación se muestra el script de PHP para descargar el archivo. No hice ningún cambio en este fragmento de código para el tamaño de archivo grande.

// Begin writing headers 

      ob_clean(); // Clear any previously written headers in the output buffer 

      if($filetype=='application/zip') 
      { 
       if(ini_get('zlib.output_compression')) 
        ini_set('zlib.output_compression', 'Off'); 
       $fp = @fopen($filepath, 'rb'); 
       if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE")) 
       { 
        header('Content-Type: "$content_type"'); 
        header('Content-Disposition: attachment; filename="'.$filename.'"'); 
        header('Expires: 0'); 
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); 
        header("Content-Transfer-Encoding: binary"); 
        header('Pragma: public'); 
        header("Content-Length: ".filesize(trim($filepath))); 
       } 
       else 
       { 
        header('Content-Type: "$content_type"'); 
        header('Content-Disposition: attachment; filename="'.$filename.'"'); 
        header("Content-Transfer-Encoding: binary"); 
        header('Expires: 0'); 
        header('Pragma: no-cache'); 
        header("Content-Length: ".filesize(trim($filepath))); 
       } 

       fpassthru($fp); 
       fclose($fp); 
      } 
      elseif($filetype=='audio'|| $filetype=='video') 
      { 
       global $mosConfig_absolute_path,$my; 
       ob_clean(); 
       header("Pragma: public"); 
       header('Expires: 0'); 
       header('Cache-Control: no-store, no-cache, must-revalidate'); 
       header('Cache-Control: pre-check=0, post-check=0, max-age=0'); 
       header("Cache-Control: public");  
       header("Content-Description: File Transfer"); 
       header("Content-Type: application/force-download"); 
       header("Content-Type: $content_type"); 
       header("Content-Length: ".filesize(trim($filepath))); 
       header("Content-Disposition: attachment; filename=\"$filename\""); 
       // Force the download   
       header("Content-Transfer-Encoding: binary");    
       @readfile($filepath); 
      } 
       else{ // for all other types of files except zip,audio/video 
       ob_clean(); 
       header("Pragma: public"); 
       header('Expires: 0'); 
       header('Cache-Control: no-store, no-cache, must-revalidate'); 
       header('Cache-Control: pre-check=0, post-check=0, max-age=0'); 
       header("Cache-Control: public");  
       header("Content-Description: File Transfer"); 
       header("Content-Type: $content_type"); 
       header("Content-Length: ".filesize(trim($filepath))); 
       header("Content-Disposition: attachment; filename=\"$filename\""); 
       // Force the download   
       header("Content-Transfer-Encoding: binary");    
       @readfile($filepath);  
      } 
      exit; 
Cuestiones relacionadas