link from force existing php header force-download

from - Descarga de fuerza a prueba de idiotas y entre navegadores en PHP



php force download pdf (7)

Hay una cosa que me parece rara: estás llamando a ob_end_flush() al comienzo de la función. Esto realmente limpia el buffer de salida, pero también lo envía todo al cliente primero (supongo que incluye Content-Headers establecidos por CodeIgniter). Cambie la llamada a ob_end_clean() , borra el búfer y lo descarta. Esto le dará un comienzo limpio para generar sus propios encabezados.

Otro consejo:

En lugar de leer el archivo como una secuencia y pasarlo en bloque, podría probar esta función:

// ... if (file_exists("dir-with-files/$filename")) { readfile($file); }

Esto se ocupa de casi todo.

Estoy usando la descarga forzada para descargar principalmente fotos y mp3 en el sitio que hice ( http://pr1pad.kissyour.net ) - para realizar un seguimiento de las descargas en Google Analytics, en la base de datos y para ocultar la ruta de descarga real:

Es esto:

extending CI model ... - bunch of code function _fullread ($sd, $len) { $ret = ''''; $read = 0; while ($read < $len && ($buf = fread($sd, $len - $read))) { $read += strlen($buf); $ret .= $buf; } return $ret; } function download(){ /* DOWNLOAD ITSELF */ ini_set(''memory_limit'', ''160M''); apache_setenv(''no-gzip'', ''1''); ob_end_flush(); header("Pragma: public"); header("Expires: 0"); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); header("Cache-Control: public",FALSE); header("Content-Description: File Transfer"); header("Content-type: application/octet-stream"); if (isset($_SERVER[''HTTP_USER_AGENT'']) && (strpos($_SERVER[''HTTP_USER_AGENT''], ''MSIE'') !== false)) header(''Content-Type: application/force-download''); //IE HEADER header("Accept-Ranges: bytes"); header("Content-Disposition: attachment; filename=/"" . basename("dir-with- files/".$filename) . "/";"); header("Content-Transfer-Encoding: binary"); header("Content-Length: " . filesize("dir-with-files/".$filename)); // Send file for download if ($stream = fopen("dir-with-files/$filename", ''rb'')){ while(!feof($stream) && connection_status() == 0){ //reset time limit for big files set_time_limit(0); print($this->_fullread($stream,1024*16)); flush(); } fclose($stream); } }

Está en LAMP con CI 1.7.2 - Es mi propio método creado a partir de varios procedimientos a través de Internet, porque durante el desarrollo surgieron estos problemas: - límite de servidor . ini_set no me ayudó, así que usé _fullread almacenado en lugar de _fullread normal, que se usó como @readonly - ob_end_flush (), porque el sitio está en CI1.7.2 y necesitaba limpiar el búfer

Ahora ... No funciona. Lo hizo, luego dejó de mostrar el tamaño esperado / tiempo de descarga - Intenté limpiarlo y mientras limpiaba el código, sucedió algo, no sé qué y en ninguna versión anterior, no funcionó (no cambio en la configuración en absoluto) - edit : do not work = muestra todo en la ventana del navegador.

Así que dije, atorníllalo, miraré aquí.

Entonces, básicamente busco un script o función, que puedo poner en mi modelo de salida y haré:

  • Descarga de fuerza de llamada (en la descarga de inicio de Chrome, en IE, FF, Safari abre el modal abrir / guardar / cancelar)
  • Mostrar el tamaño del archivo y el tiempo estimado de dl (depende del navegador, lo sé, pero primero, el navegador debe saber el tamaño del archivo)
  • WORK (¡probado y confirmado!) En IE6,7,8, FF3, Opera, Chrome & y safari en PC + Mac (Linux ... en realidad no me importa) - eso es para la parte del encabezado
  • en el servidor, también tengo algo así como un límite de memoria de 56 MB, que no puedo agregar, por lo que también es importante

Gracias de antemano.

Editar : Ahora me siento más atormentado que nunca / antes, desde que intenté forzar la descarga con .htaccess - mientras funcionaba, tenía algunos problemas menores / importantes (elige el tuyo)

  • mostró el camino completo (menor para mí)
  • espera hasta que finalice la descarga completa (que se muestra como "conexión") y luego simplemente muestra que está descargando, y descarga en un segundo (importante para mí)

Ahora, aunque eliminé .htaccess, todavía espera hasta que se complete la descarga (como si estuviera descargando primero en la memoria caché) y acaba de connected y muestra el diálogo abrir / guardar.


Si vas a hacer este tipo de método "Echo it out with php", entonces no podrás mostrar a tus usuarios el tiempo restante o el tamaño esperado. ¿Por qué? Porque si el navegador intenta reanudar la descarga en el medio, no tiene manera de manejar ese caso en PHP.

Si tiene una descarga normal de archivos, Apache puede admitir descargas reanudadas a través de HTTP, pero en el caso de que se pause una descarga, Apache no tiene forma de averiguar en qué lugar del script se estaban ejecutando las cosas cuando un cliente solicita el siguiente fragmento.

Esencialmente, cuando un navegador hace una pausa en una descarga, terminará por completo la conexión al servidor web. Cuando reanuda la descarga, la conexión se vuelve a abrir, y la solicitud contiene una bandera que dice "Comience desde el byte número X". Pero para el servidor web que mira su PHP arriba, ¿de dónde viene el byte X?

Si bien, en teoría, es posible que el servidor identifique dónde reanudar el script en caso de una descarga interrumpida, Apache no intenta averiguar dónde reanudarlo. Como resultado, el encabezado enviado al navegador indica que el servidor no admite reanudar, lo que desactiva los elementos esperados de límite de tiempo y tamaño de archivo en la mayoría de los principales navegadores.

EDITAR: Parece que puedes manejar este caso, pero tomará MUCHO código de tu parte. Ver http://www.php.net/manual/en/function.fread.php#84115 .


Solo un tiro en la oscuridad ... cada encabezado que envío en mi código de "descarga de fuerza" (que no está tan probado como el suyo) es el mismo que el suyo, excepto que llamo a: header ("Cache-Control: private" ,falso);

en lugar de: encabezado ("Cache-Control: public", FALSE);

No sé si eso ayudará o no.


print($this->_fullread($stream,1024*16));

Supongo que _fullread está dentro de una clase? Si el código es similar al anterior, entonces $this-> no funcionaría.

¿Muestra el contenido del archivo en la pantalla si usted comentó todas las cosas del encabezado?


Entonces, utilicé este código (es una versión modificada de la descarga http de reanudación que se encuentra en Internet)

function _output_file($file, $path) { $size = filesize($path.$file); @ob_end_clean(); //turn off output buffering to decrease cpu usage // required for IE, otherwise Content-Disposition may be ignored if(ini_get(''zlib.output_compression'')) ini_set(''zlib.output_compression'', ''Off''); header(''Content-Type: application/force-download''); header(''Content-Disposition: attachment; filename="''.basename($file).''"''); header("Content-Transfer-Encoding: binary"); header(''Accept-Ranges: bytes''); /* The three lines below basically make the download non-cacheable */ header("Cache-control: no-cache, pre-check=0, post-check=0"); header("Cache-control: private"); header(''Pragma: private''); header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // multipart-download and download resuming support 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); } /* output the file itself */ $chunksize = 1*(1024*1024); //you may want to change this $bytes_send = 0; if ($file = fopen($path.$file, ''rb'')) { if(isset($_SERVER[''HTTP_RANGE''])) fseek($file, $range); while (!feof($file) && (!connection_aborted()) && ($bytes_send<$new_length) ) { $buffer = fread($file, $chunksize); print($buffer); //echo($buffer); // is also possible flush(); $bytes_send += strlen($buffer); } fclose($file); } else die(''Error - can not open file.''); die(); }

y luego en el modelo:

function download_file($filename){ /* DOWNLOAD */ $path = "datadirwithmyfiles/"; //directory //track analytics include(''includes/Galvanize.php''); //great plugin $GA = new Galvanize(''UA-XXXXXXX-7''); $GA->trackPageView(); $this->_output_file($filename, $path); }

Funciona como se esperaba en todos los navegadores mencionados en Win / MAC: hasta el momento, no hay problemas con él.


De acuerdo, esta es una vieja pregunta y Adam ya aceptó su propia respuesta, así que presumiblemente consiguió que esto funcionara para él, pero no explicó por qué funcionó. Una cosa que noté fue en la pregunta que usó los encabezados:

header("Pragma: public"); header("Cache-Control: public",FALSE);

Mientras que en la solución que utilizó:

header("Cache-control: private"); header(''Pragma: private'');

No explicó por qué los cambió, pero sospecho que se relaciona con el uso de SSL. Hace poco resolví un problema similar en el software que necesita habilitar la descarga tanto en HTTP como en HTTPS, utilizando lo siguiente para agregar el encabezado correcto:

if(!empty($_SERVER[''HTTPS'']) && $_SERVER[''HTTPS''] !== ''off'' || $_SERVER[''SERVER_PORT''] == 443) { header("Cache-control: private"); header(''Pragma: private''); } else { header(''Pragma: public''); }

Esperemos que alguien encuentre la información en esta respuesta una adición útil a la anterior.


En lugar de tratar de ocultar su ruta de descarga del mundo, hágalo inaccesible desde el exterior y solo acceda a los archivos con el script anterior. para hacerlo, coloque un archivo htaccess (un archivo de texto llamado ''.htaccess'', no olvide el punto inicial) en el directorio. El contenido del htaccess sería este:

order deny,allow deny from all allow from localhost

Ahora, intentar acceder a la ruta desde * world hará que el servidor web cree un 401 prohibido.

La seguridad a través de la oscuridad no es lo que quieres.