Как заставить PHP генерировать Chunked-ответ

Я искал эту проблему, но ответа на нее нет.

Я хочу, чтобы мой PHP-скрипт генерировал HTTP-ответ в chunked ( http://en.wikipedia.org/wiki/Chunked_transfer_encoding ). Как это сделать?

Обновление: я понял это. Я должен указать заголовок Transfer-encoding и очистить его.

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

Необходим флеш. В противном случае будет создан заголовок Content-Length.

И я должен сам сделать куски. С помощью вспомогательной функции это не сложно.

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

Рабочий образец от 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> 

Это немного изменилось … если вы не возражаете против больших кусочков, (0x1000 октетов или около того), то да, PHP сделает их.

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

PHP будет генерировать пронумерованные разделы и т. Д.

Если вы хотите отправить небольшие кусочки, как вы могли бы сделать с клиентом AJAX … ну, я объединил вопрос с OPs, с некоторыми исследованиями на PHP.NET, и похоже, что он был на хорошем уровне ,

$ 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 

Независимо от того, будет ли он в конечном итоге выжать его из собственного (неправильного) количества блоков, еще не видно … но я не видел никаких признаков этого.

 <?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)); } ?> 

Ответ PHP всегда будет разбит на разделы, если вы не укажете заголовок длины содержимого, и произойдет сбой. (что произойдет автоматически после x байтов, просто не знаю точно, сколько).

Это странная вещь, о которой нужно заботиться. Это какое-то учебное / учебное упражнение или есть проблема реального мира, которую вы пытаетесь решить?

Вы должны иметь возможность использовать:

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

но вы должны убедиться, что результат соответствует спецификациям.

Более простой способ (без создания собственных блоков данных) – использовать только flush ():

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

Это сработало для меня, как показано в следующем HTTP-запросе и ответе.

Запрос:

 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: */* .... 

Отклик:

 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)..XP 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 .... 

Буфер вывода не будет отправлен в браузер до тех пор, пока он не будет заполнен. Размер по умолчанию – 4096 байт. Таким образом, вам нужно либо изменить размер буфера, либо заполнить ваши куски. Кроме того, браузер должен иметь собственный минимальный буфер до отображения страницы. Обратите внимание, что согласно статье Википедии о закодированном кодировании, отправка фрагмента 0\r\n\r\n завершает ответ.

Если вы хотите изменить настройку размера буферизации вывода, я прочитал, что вы не можете использовать ini_set('output_buffering', $value) . Вместо этого измените настройку буферизации вывода, добавив в файл php.ini следующее.

 php_value output_buffering 1024 

Вот пример заполнения ваших кусков

 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(); 

Вот короткая версия, которая демонстрирует 0\r\n\r\n\ завершение вывода:

 $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'); 

Решение Андрея Ф. работало для меня. Вот упрощенная версия его ответа:

 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); } 

Хотя я не понимаю, зачем ob_flush() вызов ob_flush() . Если кто-то знает, прокомментируйте.

Если размер достаточно большой, Apache сделает это за вас. Если вы работаете с ob_gzhandler, я думаю, что это не имеет смысла. Лучше вы выпустите буфер как можно быстрее. Если PHP действительно не отправляет автоматически заголовок длины контента или длину контента из несжатого контента:

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

Вы можете уменьшить размер буфера с помощью mod_buffer от apache (может быть, с ini_set PHP).

PS: Если размер достаточно большой и длина контента неизвестна, содержимое будет отправляться в куски как временное.

Мне нужно было сделать еще две вещи, чтобы заставить это работать. Вызов ob_start, если объект еще не запущен. А также вывести длинную пустую строку. Браузеру требуется определенное количество контента для отправки в его первом фрагменте.

 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();