2009-01-11 34 views
11

Así que estoy tratando de servir archivos de gran tamaño a través de un script PHP, no están en un directorio accesible, por lo que esta es la mejor forma de proporcionar acceso a ellos.Sirviendo archivos de gran tamaño con PHP

La única manera en que pude pensar fuera de bat para servir este archivo es cargándolo en la memoria (fopen, fread, etc.), estableciendo los datos del encabezado en el tipo MIME apropiado, y luego solo repitiendo todo el contenido del archivo.

El problema con esto es que tengo que cargar estos ~ 700MB archivos en la memoria de una vez, y mantener todo allí hasta que finalice la descarga. Sería bueno si pudiera transmitir las partes que necesito mientras se están descargando.

¿Alguna idea?

+0

¿No están accesibles debido a la autenticación? – mark

+0

entre otras cosas, sí – Adam

+0

http://stackoverflow.com/questions/3697748/fastest-way-to-serve-a-file-using-php –

Respuesta

23

No es necesario que lo lea todo, solo ingrese un bucle que lo lea, digamos, fragmentos de 32Kb y envíelo como salida. Mejor aún, utilizar fpassthru que no hace lo mismo para usted ....

$name = 'mybigfile.zip'; 
$fp = fopen($name, 'rb'); 

// send the right headers 
header("Content-Type: application/zip"); 
header("Content-Length: " . filesize($name)); 

// dump the file and stop the script 
fpassthru($fp); 
exit; 

incluso menos líneas si utiliza readfile, que no necesita la llamada fopen ...

$name = 'mybigfile.zip'; 

// send the right headers 
header("Content-Type: application/zip"); 
header("Content-Length: " . filesize($name)); 

// dump the file and stop the script 
readfile($name); 
exit; 

Si desea obtener aún más lindo, puede admitir el encabezado Content-Range que permite a los clientes solicitar un rango de bytes particular de su archivo. Esto es particularmente útil para servir archivos PDF a Adobe Acrobat, que solo solicita los fragmentos del archivo que necesita para representar la página actual. Es un poco complicado, pero see this for an example.

+0

¡Gracias! No me di cuenta de que podía salirme con Content-Range con Adobe. – David

+0

fpassthru() ha sido [reportado] (http://www.php.net/manual/en/function.fpassthru.php) como un problema de memoria ya que carga todo el archivo en la memoria. No es bueno para archivos grandes. – cnvzmxcvmcx

1

Eche un vistazo a fpassthru(). En versiones más recientes de PHP esto debería servir a los archivos sin mantenerlos en la memoria, como dice this comment.

6

Si bien fpassthru() ha sido mi primera opción en el pasado, el manual de PHP en realidad recomienda * usar readfile() en su lugar, si solo está volcando el archivo tal cual al cliente.

* "Si solo desea volcar el contenido de un archivo en el búfer de salida, sin primero modificarlo o buscar un desplazamiento en particular, puede usar readfile(), que le ahorra el fopen() llamada." - PHP manual

10

La mejor manera de enviar archivos grandes con php es el encabezado . Permite que el servidor web sirva archivos mucho más rápido a través de mecanismos de copia cero como sendfile(2). Es compatible con lighttpd y apache con un plugin.

Ejemplo:

$file = "/absolute/path/to/file"; // can be protected by .htaccess 
header('X-Sendfile: '.$file); 
header('Content-type: application/octet-stream'); 
header('Content-Disposition: attachment; filename="'.basename($file).'"'); 
// other headers ... 
exit; 

El servidor lee la cabecera y envía el archivo.

0

Una de las ventajas de fpassthru() es que esta función no solo funciona con archivos sino con cualquier identificador válido. Zócalo, por ejemplo.

Y readfile() debe ser un poco más rápido, ya que es posible usar el mecanismo de caché del sistema operativo, si es posible (como file_get_contents()).

Un consejo más.fpassthru() mantiene el controlador abierto, hasta que el cliente obtiene el contenido (lo que puede requerir bastante tiempo en la conexión lenta), por lo que debe usar algún mecanismo de bloqueo si es posible escribir en paralelo en este archivo.

0

Las respuestas de Python son todas buenas. ¿Pero hay alguna razón por la que no se puede hacer un directorio accesible en la web que contenga enlaces simbólicos a los archivos reales? Puede llevar algo de configuración de servidor adicional, pero debería funcionar.

2

Si el servidor web no puede acceder a sus archivos porque la ruta no está en su directorio de servicios web (htdocs) puede hacer un enlace simbólico (enlace simbólico) a esa carpeta en su directorio de servicio web para evitar pasar todo el tráfico a través de php.

Puede hacer algo como esto

ln -s /home/files/big_files_folder /home/www/htdocs 

usando PHP para servir archivos estáticos es mucho más lento, si usted tiene un alto tráfico, el consumo de memoria será muy grande y no puede manejar un gran número de solicitudes .

1

Extraño, ni fpassthru() ni readfile() lo hicieron por mí, siempre tuvieron un error de memoria. recurrí a usar de tránsito() sin la 'f':

$name = 'mybigfile.zip'; 
// send the right headers 
header("Content-Type: application/zip"); 
header("Content-Length: " . filesize($name)); 
// dump the file and stop the script 
passthru('/bin/cat '.$filename); 
exit; 

esta ejecutivos de 'gato' de comandos de Unix y enviar su salida al navegador.

comment for slim: la razón por la que simplemente no pones un enlace simbólico en alguna parte es que el espacio web es SEGURIDAD.

Cuestiones relacionadas