我正在加载一个由PHP动态生成的特别大的JSON字符串。为了向用户提供一些反馈,我想显示下载进度。
我已经找到了代码,它适用于静态内容,如图像,JS文件等。但是,它似乎不适用于动态文件。
这是有道理的,因为动态文件没有可预测的内容长度,但即使我在PHP中添加它:
ob_start(function($c) {
header("Content-Length: ".strlen($c));
return $c;
});
它仍然不会发送标题(但如果我添加任何其他标题,它可以正常工作)。
有没有办法强制Apache发送Content-Length
标头?目前我唯一的选择是将输出保存到临时文件并改为重定向到它。这样可行,但它有点混乱,所以如果可能的话我宁愿避免它。
答案 0 :(得分:10)
我有类似的问题,但在我的情况下,Apache没有发送Content-Length头,因为响应是gzip压缩的。当我禁用压缩时,计算内容长度并正确发送。
以下是htaccess设置,仅为swf文件禁用gzip压缩
<FilesMatch "\.swf$">
SetEnv no-gzip 1
</FilesMatch>
答案 1 :(得分:0)
我最好的猜测是modules/http/http_filters
中有一些更改,因为Apache默认发送Content-Length
。
答案 2 :(得分:0)
HTTP协议需要一种方法来确定响应何时结束。根据
4.4消息长度
消息的传输长度是消息体的长度 它出现在信息中;也就是说,在任何转移编码之后 已被应用。当消息中包含消息正文时, 该身体的转移长度由以下之一确定(in 优先顺序):
1.任何响应消息“绝不”包含消息体(例如1xx,204和304响应以及对HEAD请求的任何响应) 始终由标题字段后的第一个空行终止, 无论消息中存在哪个实体标题字段。
2.如果存在Transfer-Encoding标头字段(第14.41节)并且具有除“identity”以外的任何值,则传输长度为 除非使用“分块”转移编码(第3.6节)定义 通过关闭连接终止消息。
3.如果存在Content-Length头字段(第14.13节),则其在OCTET中的十进制值表示实体长度和 转发长度。如果,则不得发送Content-Length头字段 这两个长度是不同的(即,如果转移编码
header field is present). If a message is received with both a Transfer-Encoding header field and a Content-Length header field, the latter MUST be ignored.
4.如果消息使用媒体类型“multipart / byteranges”,并且未指定transfer-length,则此自定义 媒体类型定义传输长度。这种媒体类型绝不可以 除非发件人知道收件人可以解析它,否则使用它;该 存在于具有多个字节范围的Range标头的请求中 来自1.1客户端的说明符暗示客户端可以解析 multipart / byteranges响应。
A range header might be forwarded by a 1.0 proxy that does not understand multipart/byteranges; in this case the server MUST delimit the message using methods defined in items 1,3 or 5 of this section.
5.服务器关闭连接。 (关闭连接不能用于指示请求正文的结束,因为这将离开 服务器无法发回响应。)
为了与HTTP / 1.0应用程序,HTTP / 1.1请求兼容 包含消息体必须包含有效的Content-Length头 字段,除非已知服务器符合HTTP / 1.1。如果一个 请求包含一个消息体,并且没有给出Content-Length, 如果无法确定,服务器应该响应400(错误请求) 消息的长度,或者如果愿意,可以使用411(需要的长度) 坚持收到有效的内容长度。
接收实体的所有HTTP / 1.1应用程序必须接受 “分块”转移编码(第3.6节),从而允许这种机制 在无法确定消息长度时用于消息 提前。
消息不得包含Content-Length头字段和a 非身份转移编码。如果消息确实包含非 身份转移编码,必须忽略内容长度。
在消息正文的消息中给出Content-Length时 允许,其字段值必须与OCTET的数量完全匹配 消息体。 HTTP / 1.1用户代理必须在何时通知用户 收到并检测到无效长度。
您不能随意添加标题并期望它们被遵守(其他标题可以覆盖)。您需要控制首先生成的所有可能重写的标题。
根据这个问题(How to make PHP generate Chunked response),强制“Chunked”的一个好方法是设置Transfer-Encoding和flush
。也许这两个并不是严格需要的。在开始缓冲之前,是否会有任何无关的冲洗?
答案 3 :(得分:0)
来自ob_start
文档:
This function will turn output buffering on. While output buffering is active no output is sent from the script (other than headers), instead the output is stored in an internal buffer.
注意“除了标题”位 - 在刷新(或丢弃)缓冲区之前不会调用ob_start()
回调,此时使用已为时已晚header()
。我猜你没有打开错误日志,我在错误日志中看到了这一点:
PHP Warning: Cannot modify header information - headers already sent
in /usr/local/apache2/apps/testing/test2.php on line 6
回调允许您在发送之前修改缓冲区,但因为它只包含正文数据,所以您也不能添加新标题(如果您尝试,它将出现在内容中,对于每个块进行分块传输时)。
一旦您知道尺寸,在任何其他输出之前,您的代码应该调用header("Content-Length: ...")
。如果找到Content-Length:
标题,则不会进行分块传输。
如果您确实需要强制使用HTTP / 1.0(您没有),则可以使用特殊变量在httpd.conf
中执行此操作:
SetEnv downgrade-1.0 1
SetEnv force-response-1.0 1
您可以将这些内容放在<Location>
或<LocationMatch>
中,或使用mod_rewrite
的“env
”标记进行更多控制。
答案 4 :(得分:0)
我有一些大约1MB的JS文件,它们没有Content-Length标头(与其他较小的JS文件不同)。 Content-Length标头在HTTP / 2中不是必需的,并且Internet连接有些不稳定,这导致很难调试的错误,其中有时仅部分交付文件。解决方案是增加DeflateBufferSize https://httpd.apache.org/docs/2.4/mod/mod_deflate.html#DeflateBufferSize