2011-07-08 18 views
21

He visto muchas preguntas sobre cómo usar PHP de manera eficiente para descargar archivos en lugar de permitir solicitudes HTTP directas (para mantener los archivos seguros, realizar un seguimiento de las descargas, etc.).¿Por qué readfile() agota la memoria PHP?

La respuesta casi siempre es PHP readfile().

PERO, aunque funciona muy bien durante las pruebas con archivos de gran tamaño, cuando está en un sitio vivo con cientos de usuarios, descargas comienzan a colgar y PHP los límites de memoria están agotados

¿De qué se trata cómo funciona readfile() que hace que la memoria explote tanto cuando hay mucho tráfico? Pensé que se suponía que debía evitar el uso intensivo de la memoria PHP escribiendo directamente en el búfer de salida.

EDIT: (. Para aclarar, estoy buscando un "por qué", no "¿qué puedo hacer" Creo que mod_xsendfile de Apache es la mejor manera de eludir)

+1

¿Tiene búferes de salida de PHP en (a través de 'ob_start()')? Si es así, PHP atrapará el contenido del archivo y causará problemas de memoria. – ceejayoz

+1

Usando el ejemplo en el documento PHP enlazado, no lo hago - Tengo ob_clean(); enjuagar(); readfile ($ archivo); – tmsimont

Respuesta

4
Description 
int readfile (string $filename [, bool $use_include_path = false [, resource $context ]]) 
Reads a file and writes it to the output buffer*. 

PHP tiene que lea el archivo y lo escribe en el búfer de salida. Por lo tanto, para archivos de 300Mb, sin importar la implementación que hayas escrito (por muchos segmentos pequeños, o por 1 gran parte), PHP tiene que leer hasta 300Mb de archivos eventualmente.

Si el usuario tiene que descargar el archivo, habrá un problema. (En un servidor, los proveedores de hosting limitarán la memoria dada a cada usuario de alojamiento. Con dicha memoria limitada, usar el búfer no va a ser una buena idea.)

Creo que usar el enlace directo para descargar un archivo es mucho mejor enfoque para grandes archivos.

+0

Entonces, si PHP está "escribiendo en el búfer de salida" para UserA - ¿está usando memoria que luego no está disponible para UserB y UserC? Lo que lleva a un eventual uso excesivo de la memoria? – tmsimont

+0

Sí. Como PHP no sabe cuando el otro usuario va a descargar el mismo archivo, PHP simplemente leerá el archivo en el búfer y lo escribirá a un solicitante lo antes posible ... Si 100 usuarios solicitan el mismo archivo de 10 MB, el uso de la memoria probablemente sea 1000MB. – user482594

+0

tiene sentido para mí ... por lo que readfile() no acaba de hacer el truco, y debe evitarse en situaciones de tráfico intenso. Gracias. – tmsimont

0

Bueno, es una función con mucha memoria. Me gustaría conectar a los usuarios a un servidor estático que tiene un conjunto de reglas específicas para controlar las descargas en lugar de usar readfile().

Si no es una opción, agregue más RAM para satisfacer la carga o introduzca el sistema de colas que controla el uso del servidor.

1

Es posible que desee desactivar por completo el almacenamiento en búfer de salida para esa ubicación en particular, utilizando la directiva PHP's output_buffering configuration.

ejemplo Apache:

<Directory "/your/downloadable/files"> 
    ... 
    php_admin_value output_buffering "0" 
    ... 
</Directory> 

"Off" como el valor parece funcionar tan bien, mientras que en realidad debería generar un error. Al menos de acuerdo con how other types are converted to booleans in PHP.* encoge *

2

Si tiene búferes de salida de usar ob_end_flush() justo antes de la llamada a ReadFile()

header(...); 
ob_end_flush(); 
@readfile($file); 
+0

He intentado añadir esto antes de hacer readfile en un archivo de 2 GB, pero aún así fue por arriba del límite de memoria – NDM

+0

Como se mencionó anteriormente, el almacenamiento en búfer de salida no es el problema. – reggie

1

se le ocurrió esta idea en el pasado (como parte de mi biblioteca) para evitar el uso de memoria alta:

function suTunnelStream($sUrl, $sMimeType, $sCharType = null) 
{ 
    $f = @fopen($sUrl, 'rb'); 
    if($f === false) 
    { return false; } 

    $b = false; 
    $u = true; 

    while($u !== false && !feof($f)) 
    { 
    $u = @fread($f, 1024); 
    if($u !== false) 
    { 
     if(!$b) 
     { $b = true; 
     suClearOutputBuffers(); 
     suCachedHeader(0, $sMimeType, $sCharType, null, !suIsValidString($sCharType)?('content-disposition: attachment; filename="'.suUniqueId($sUrl).'"'):null); 
     } 
     echo $u; 
    } 
} 

    @fclose($f); 
    return ($b && $u !== false); 
} 

Quizás esto le puede dar un poco de inspiración.

1

Como se menciona aquí: "Allowed memory .. exhausted" when using readfile, el siguiente bloque de código en la parte superior del archivo php hizo el truco para mí.

Esto comprueba si búferes de salida PHP está activa. Si es así, lo apaga.

if (ob_get_level()) { 
    ob_end_clean(); 
} 
+0

Dado que la pregunta es "por qué", creo que debería proporcionar una explicación en lugar de una solución. Después de todo, básicamente estás copiando una respuesta existente. –

+0

Ah sí. Seguí adelante y agregué una breve explicación. –

Cuestiones relacionadas