如何让 PHP 生成分块响应

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2481858/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 06:39:15  来源:igfitidea点击:

How to make PHP generate Chunked response

phphttpchunked-encoding

提问by Morgan Cheng

I googled for this problem but there is no answer for it.

我用谷歌搜索了这个问题,但没有答案。

I want my PHP script to generate HTTP response in chunked( http://en.wikipedia.org/wiki/Chunked_transfer_encoding). How to do it?

我希望我的 PHP 脚本在分块(http://en.wikipedia.org/wiki/Chunked_transfer_encoding)中生成 HTTP 响应。怎么做?

Update: I figured it out. I have to specify Transfer-encodingheader and flush it.

更新:我想通了。我必须指定Transfer-encoding标头并刷新它。

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

The flush is necessary. Otherwise, Content-Length header will be generated.

冲洗是必要的。否则,将生成 Content-Length 标头。

And, I have to make chunks by myself. With a helper function, it is not hard.

而且,我必须自己做块。有了辅助函数,就不难了。

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

回答by Evert

A PHP response will always be chunked if you don't specify a content-length header, and a flush occurs. (which will happen automatically after x bytes, just don't know exactly how much).

如果您不指定内容长度标头,PHP 响应将始终被分块,并且会发生刷新。(这将在 x 字节后自动发生,只是不知道确切多少)。

This is a weird thing to care about. Is this some kind of academic/learning exercise or is there a real world problem you're trying to solve?

这是一个奇怪的事情要关心。这是某种学术/学习练习还是您正在尝试解决的现实问题?

回答by Orwellophile

This has gotten a little vauge... if you don't mind big assed chunks, (0x1000 octets or so), then yes, PHP will make them.

这已经变得有点 vauge ......如果你不介意大块,(0x1000个八位字节左右),那么是的,PHP会制作它们。

<?php

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

PHP will generated the numbered sections, etc.

PHP 将生成编号的部分等。

If you want to send tiny little chunks, as you might do with an AJAX client... well, I've combined the OPs question, with some research on PHP.NET, and it does look like he was on to a good thing.

如果你想发送很小的块,就像你可能用 AJAX 客户端做的那样......好吧,我已经将 OP 问题与对 PHP.NET 的一些研究结合起来,看起来他确实是一件好事.

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

$ echo -en "GET /chunked/ HTTP/1.1\r\nHost: ec\r\n\r\n" | 本地主机 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

Whether or not it will eventually squeeze out it out's own (incorrect) chunk count, remains to be seen... but I saw no sign of it.

它是否最终会挤出它自己的(不正确的)块数,还有待观察......但我没有看到它的迹象。

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

回答by Andriy F.

Working sample as of 16.02.2013

截至 2013 年 2 月 16 日的工作样本

<?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>

回答by skibulk

The output buffer will not be sent to the browser until it is full. The default size is 4096bytes. So you either need to change the buffer size or pad your chunks. Also, the browser may have it's own minimum buffer before the page is displayed. Note that according to the Wikipedia Articleabout chunked encoding, sending a 0\r\n\r\nchunk terminates the response.

输出缓冲区在满之前不会发送到浏览器。默认大小为4096字节。因此,您需要更改缓冲区大小或填充块。此外,在页面显示之前,浏览器可能有自己的最小缓冲区。请注意,根据维基百科关于分块编码的文章,发送0\r\n\r\n块会终止响应。

If you want to change the output buffering size setting, I read that you cannot use ini_set('output_buffering', $value). Instead, change the output buffering setting by adding the following to your php.ini file.

如果您想更改输出缓冲大小设置,我读到您不能使用ini_set('output_buffering', $value). 相反,通过将以下内容添加到 php.ini 文件来更改输出缓冲设置。

php_value output_buffering 1024

Here's an example of padding your chunks

这是填充块的示例

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

Here's a short version that demonstrates 0\r\n\r\n\terminating the output:

这是一个演示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');

回答by Andre

You should be able to use:

您应该能够使用:

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

but you'll have to ensure yourself that the output follows the specifications.

但您必须确保自己的输出符合规范。

回答by Wade

A simpler way (without creating your own data chunks) is to only use flush():

一种更简单的方法(不创建自己的数据块)是只使用flush():

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

This worked for me as shown in the following HTTP request and response.

这对我有用,如下面的 HTTP 请求和响应所示。

Request:

要求:

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

Response:

回复:

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

回答by Ohas

Andriy F.'s solutionworked for me. Here's a simplified version of his answer:

Andriy F. 的解决方案对我有用。这是他的答案的简化版本:

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

Although I don't understand why the ob_flush()call is needed. If someone knows, please comment.

虽然我不明白为什么ob_flush()需要打电话。如果有人知道,请评论。

回答by B.F.

If the size is big enough, Apache do it for you. If you work with ob_gzhandler, I think it makes no sense. Better you let the buffer out as fast as possible. If PHP really does not send automatically the content-length header or the content-length is from the uncompressed content:

如果大小足够大,Apache 会为您完成。如果你和 ob_gzhandler 一起工作,我认为这是没有意义的。最好尽快释放缓冲区。如果 PHP 真的没有自动发送 content-length 头或者 content-length 来自未压缩的内容:

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

You could reduce buffer size with apache's mod_buffer (may be with PHP's ini_set).

您可以使用apache的 mod_buffer (可能使用 PHP 的 ini_set)减少缓冲区大小。

PS: If the size is big enough and the content-length is unknown, the content will be send in chunks as a makeshift.

PS:如果大小足够大并且内容长度未知,则内容将作为临时文件分块发送。

回答by Thomas Welton

I needed to do two additional things to get this to work. Call ob_start if an object had not yet been started. And also echo out a long empty string. The browser requires a certain amount of content to be sent in it's first chunk it appears.

我需要做两件额外的事情才能让它发挥作用。如果对象尚未启动,则调用 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();