2010-03-20 21 views
28

Busqué en Google este problema pero no hay respuesta para él.Cómo hacer que PHP genere una respuesta fragmentada

Quiero que mi script PHP genere una respuesta HTTP en fragmentos (http://en.wikipedia.org/wiki/Chunked_transfer_encoding). ¿Cómo hacerlo?

Actualización: Me di cuenta. Debo especificar Encabezado de codificación de transferencia y lavarlo.

header("Transfer-encoding: chunked"); 
flush(); 

La descarga es necesaria. De lo contrario, se generará un encabezado Content-Length.

Y, tengo que hacer trozos por mi cuenta. Con una función auxiliar, no es difícil.

function dump_chunk($chunk) 
{ 
    echo sprintf("%x\r\n", strlen($chunk)); 
    echo $chunk; 
    echo "\r\n"; 
} 
+0

Puede limpiar después de cada trozo. – Gumbo

Respuesta

2

Usted debe ser capaz de usar:

<?php header("Transfer-Encoding: chunked"); 

sino que tendrá que asegurarse a sí mismo que la salida sigue las especificaciones.

+0

Parece que no es suficiente. Tengo que hacer flush() justo después de la última instrucción header() para asegurarme de que no se genere el encabezado "Content-Length". –

+0

A veces debe desactivar gz-compress en el servidor y enviar 'header (" Content-Encoding: identity ");' – bato3

2

Una manera más sencilla (sin crear sus propios fragmentos de datos) es utilizar solamente flush():

<?php 
$fp = fopen('data.bin', "rb"); 
flush(); 
fpassthru($fp); 
?> 

Esto funcionó para mí como se muestra en la siguiente petición y respuesta HTTP.

Solicitud:

00000000 47 45 54 20 2f 67 65 74 2d 63 68 75 6e 6b 2e 70 GET /get -chunk.p 
00000010 68 70 20 48 54 54 50 2f 31 2e 31 0d 0a 55 73 65 hp HTTP/ 1.1..Use 
00000020 72 2d 41 67 65 6e 74 3a 20 63 75 72 6c 2f 37 2e r-Agent: curl/7. 
00000030 32 31 2e 37 20 28 69 36 38 36 2d 70 63 2d 6c 69 21.7 (i6 86-pc-li 
00000040 6e 75 78 2d 67 6e 75 29 20 6c 69 62 63 75 72 6c nux-gnu) libcurl 
00000050 2f 37 2e 32 31 2e 37 20 4f 70 65 6e 53 53 4c 2f /7.21.7 OpenSSL/ 
00000060 31 2e 30 2e 30 64 20 7a 6c 69 62 2f 31 2e 32 2e 1.0.0d z lib/1.2. 
00000070 35 20 6c 69 62 73 73 68 32 2f 31 2e 32 2e 37 0d 5 libssh 2/1.2.7. 
00000080 0a 48 6f 73 74 3a 20 67 61 69 61 0d 0a 41 63 63 .Host: g aia..Acc 
00000090 65 70 74 3a 20 2a 2f 2a 0d 0a 0d 0a    ept: */* .... 

Respuesta:

00000000 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d HTTP/1.1 200 OK. 
00000010 0a 44 61 74 65 3a 20 54 68 75 2c 20 32 32 20 53 .Date: T hu, 22 S 
00000020 65 70 20 32 30 31 31 20 32 33 3a 35 33 3a 30 32 ep 2011 23:53:02 
00000030 20 47 4d 54 0d 0a 53 65 72 76 65 72 3a 20 41 70 GMT..Se rver: Ap 
00000040 61 63 68 65 2f 32 2e 32 2e 38 20 28 46 65 64 6f ache/2.2 .8 (Fedo 
00000050 72 61 29 0d 0a 58 2d 50 6f 77 65 72 65 64 2d 42 ra)..X-P owered-B 
00000060 79 3a 20 50 48 50 2f 35 2e 32 2e 36 0d 0a 54 72 y: PHP/5 .2.6..Tr 
00000070 61 6e 73 66 65 72 2d 45 6e 63 6f 64 69 6e 67 3a ansfer-E ncoding: 
00000080 20 63 68 75 6e 6b 65 64 0d 0a 43 6f 6e 74 65 6e chunked ..Conten 
00000090 74 2d 54 79 70 65 3a 20 74 65 78 74 2f 68 74 6d t-Type: text/htm 
000000A0 6c 3b 20 63 68 61 72 73 65 74 3d 55 54 46 2d 38 l; chars et=UTF-8 
000000B0 0d 0a 0d 0a          .... 
000000B4 61 0d 0a 54 45 53 54 20 44 41 54 41 0a 0d 0a 30 a..TEST DATA...0 
000000C4 0d 0a 0d 0a          .... 
4

respuesta Un PHP siempre será fragmentada, si no se especifica un encabezado de longitud de contenido, y se produce un color. (que sucederá automáticamente después de x bytes, simplemente no sé exactamente cuánto).

Esto es algo raro de lo que preocuparse. ¿Es esto algún tipo de ejercicio académico/de aprendizaje o hay un problema del mundo real que estás tratando de resolver?

+0

Quiero forzar la codificación de transferencia fragmentada porque estoy probando un script de ruby ​​que se supone que puede leerlo . Entonces, si te entiendo correctamente, solo necesito un poco de color(); las líneas se ejecutan periódicamente y obligarán a php a responder en la codificación de transferencia fragmentada. ¿No es necesario generar explícitamente ningún número de tamaño de fragmento? –

+1

Apache debería manejar eso por ti solo. Básicamente, si apache no conoce la longitud por adelantado, no tiene otra opción que usar 'fragmentado' – Evert

+0

Apache almacena una cierta cantidad de salida antes de enviar. Si toda tu salida entra en el búfer, no se fragmentará, a menos que la fuerces. – sosiouxme

6

Esto tiene un poco de vauge ... si no te molestan los trozos grandes, (0x1000 octetos más o menos), entonces sí, PHP los hará.

<?php 

while (true) { 
    # output data 
    flush() 
    usleep(pow(2,18)); 
} 
?> 

PHP generado las secciones numeradas, etc.

Si desea enviar pequeños trozos pequeños, como se puede ver con un cliente AJAX ... bueno, yo he combinado la cuestión PO, con algunas investigaciones en PHP.NET, y parece que fue una buena cosa.

$ echo -en "GET/chunked/HTTP/1.1 \ r \ nHost: ec \ r \ n \ r \ n" | nc localhost 80

HTTP/1.1 200 OK 
Date: Wed, 23 May 2012 13:03:01 GMT 
Server: Apache/2.2.9 (Debian) PHP/5.3.5-1 with Suhosin-Patch mod_ssl/2.2.9 OpenSSL/0.9.8o 
X-Powered-By: PHP/5.3.5-1 
Transfer-encoding: chunked 
Content-Type: text/html 

14 
Teachers have class. 
50 
We secure our friends not by accepting favors but by doing them. 
      -- Thucydides 
48 
Vulcans never bluff. 
      -- Spock, "The Doomsday Machine", stardate 4202.1 
31 
All kings is mostly rapscallions. 
      -- Mark Twain 
41 
Reappraisal, n.: 
    An abrupt change of mind after being found out. 
49 
He who knows, does not speak. He who speaks, does not know. 
      -- Lao Tsu 

Sea o no con el tiempo se exprima hacia fuera del propio recuento trozo (incorrecta), queda por ver ... pero no vi ninguna señal de ello.

<?php 
header("Transfer-encoding: chunked"); 
@apache_setenv('no-gzip', 1); 
@ini_set('zlib.output_compression', 0); 
@ini_set('implicit_flush', 1); 
for ($i = 0; $i < ob_get_level(); $i++) ob_end_flush(); 
ob_implicit_flush(1); flush(); 

function dump_chunk($chunk) 
{ 
    printf("%x\r\n%s\r\n", strlen($chunk), $chunk); 
    flush(); 
} 

for (;;) { 
    $output = array(); 
    exec("/usr/games/fortune", $output); 
    dump_chunk(implode("\n", $output)); 
    usleep(pow(2,18)); 
} 
?> 
+0

Si su intención es deshacerse de cada buffer de salida, de esta manera solo eliminará la mitad de ellos (redondeados hacia arriba). Use 'while (ob_get_level()) {ob_end_flush();}' en su lugar.Si quiere evitar llamar repetitivamente a la función: 'for ($ i = ob_get_level(); $ i; - $ i) {ob_end_flush();}' o '$ i = ob_get_level() + 1; while (- $ i) {ob_end_flush();} '(use post-decremento si el +1 parece impar). Gracias por el ejemplo de netcat, HTTP es mucho más simple de lo que esperaba, lo cual es gracioso ya que he visto '', encabezados y cuerpos, pero generalmente se muestran por separado cuando los veo. –

-1

Necesitaba hacer dos cosas adicionales para que esto funcione. Llame a ob_start si un objeto aún no se ha iniciado. Y también resuenan una larga cadena vacía.El navegador requiere que se envíe cierta cantidad de contenido en su primer fragmento.

header('Content-Encoding', 'chunked'); 
header('Transfer-Encoding', 'chunked'); 
header('Content-Type', 'text/html'); 
header('Connection', 'keep-alive'); 

if (ob_get_level() == 0) ob_start(); 
echo str_pad('',4096)."\n"; 
ob_flush(); 
flush(); 
7

muestra Trabajando a partir del 16.02.2013

<?php 

function dump_chunk($chunk) { 
    //echo sprintf("%x\r\n", strlen($chunk)); 
    echo sprintf("\r\n"); 
    echo $chunk; 
    echo "\r\n"; 
} 

ob_start(); 
?> 
<!DOCTYPE html> 
<html> 
    <head> 
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
     <title>Title</title> 
    </head> 
    <body> 
     <?php 
     ob_end_flush(); 
     flush(); 
     ob_flush(); 
     for ($i = 0; $i < 5; $i++) { 
      sleep(1); 
      dump_chunk('Sending data chunk ' . ($i + 1) . ' of 1000 <br />'); 
      flush(); 
      ob_flush(); 
     } 
     sleep(1); // needed for last animation 
     ?> 
    </body> 
</html> 
+0

Por algún motivo, la página solo se muestra cuando se representa toda la página en lugar de aumentar. ¿Sabes cuál pudo haber sido el problema? – Pacerier

+0

¿Qué configuración estás usando? (Versión de Apache, módulos, versión de PHP)? –

+0

PHP 5.3.26, apache 2.2.24 + nginx http://www.hosting24.com/blog/index.php?read=Better-performance%21-New-server-setup%3A-Apache-%2B-Nginx % 21 & id = 12 proporcionado por el paquete de plata de hosting24 http://www.hosting24.com/features.php – Pacerier

0

Andriy F.'s solution trabajó para mí. Aquí hay una versión simplificada de su respuesta:

function dump_chunk($chunk) 
{ 
    echo $chunk; 
    flush(); 
    ob_flush(); 
} 

header('Content-Type: text/html; charset=UTF-8'); 

flush(); 

for ($i = 0; $i < 3; $i++) { 
    dump_chunk('Sending data chunk ' . ($i + 1) . ' of 1000 <br />'); 
    sleep(1); 
} 

Aunque no entiendo por qué es necesaria la llamada ob_flush(). Si alguien sabe, por favor comente.

0

Si el tamaño es lo suficientemente grande, Apache lo hace por usted. Si trabajas con ob_gzhandler, creo que no tiene sentido. Es mejor que dejes el buffer lo más rápido posible. Si PHP realmente no envía automáticamente la cabecera Content-Length o el contenido de longitud es a partir del contenido sin comprimir:

ob_start(); 
ob_start("ob_gzhandler"); 
... 
ob_end_flush(); 
header('Content-Length: '.ob_get_length()); 
ob_end_flush(); 

Se podría reducir el tamaño del búfer con mod_buffer apache 's (puede ser con ini_set PHP).

PD: Si el tamaño es lo suficientemente grande y la longitud del contenido es desconocida, el contenido se enviará en trozos como una improvisación.

1

El buffer de salida no se enviará al navegador hasta que esté lleno. El tamaño predeterminado es 4096 bytes. Entonces, o necesita cambiar el tamaño del búfer o rellenar sus trozos. Además, el navegador puede tener su propio búfer mínimo antes de que se muestre la página. Tenga en cuenta que según el Wikipedia Article sobre la codificación fragmentada, el envío de un fragmento 0\r\n\r\n finaliza la respuesta.

Si desea cambiar la configuración del tamaño de almacenamiento en búfer de salida, leo que no puede usar ini_set('output_buffering', $value). En su lugar, cambie la configuración del búfer de salida agregando lo siguiente a su archivo php.ini.

php_value output_buffering 1024 

He aquí un ejemplo de relleno de los trozos

header("Transfer-Encoding: chunked"); 
header("Content-Encoding: none"); 

// Send chunk to browser 
function send_chunk($chunk) 
{ 
    // The chunk must fill the output buffer or php won't send it 
    $chunk = str_pad($chunk, 4096); 

    printf("%x\r\n%s\r\n", strlen($chunk), $chunk); 
    flush(); 
} 

// Send your content in chunks 
for($i=0; $i<10; $i++) 
{ 
    send_chunk("This is Chunk #$i.<br>\r\n"); 
    usleep(500000); 
} 

// note that if you send an empty chunk 
// the browser won't display additional output 
echo "0\r\n\r\n"; 
flush(); 

Aquí hay una versión corta que demuestra 0\r\n\r\n\ terminación de la salida:

$output = "hello world"; 
header("Transfer-Encoding: chunked"); 
header('Content-Encoding: none'); 
printf("%x\r\n%s\r\n", strlen($output), $output); 
ob_flush(); 
print("0\r\n\r\n"); 
ob_flush(); 
flush(); 
sleep(10); 
print('POST PROCESSING'); 
Cuestiones relacionadas