如何在没有Content-Length头的情况下流式传输HTTP文件上传?

11

是否可能在不包含content-length头信息的情况下上传文件到Apache PHP服务器?

我试图流式传输我动态创建的文件作为文件上传。当我没有使用content-length头信息时,我收到了Apache返回的"501 Method Not Implemented"错误。

$sock = fsockopen($host,80,$errno, $error);
fwrite($sock, "POST $resource HTTP/1.1\r\n" .
                     "Host: $host\r\n\r\n");
fwrite($sock,fread($readHandle,filesize($file)));

如果我包含 content-length,它就能正常工作。

服务器正在从 php://input 读取数据。


如果长度标头缺失会发生什么?会有错误消息吗? - wallyk
1
无关:甜美的琼恩·雪格拉维塔 - 游戏王者之歌 FTW。 - user895378
1个回答

22
根据HTTP规范,你在技术上并不需要指定Content-Length头。来自RFC 2616 14.13的内容如下:
应用程序应使用此字段指示消息正文的传输长度,除非第4.4节中的规则禁止这样做。
然而,对于大多数服务器来说,这是一个相当标准的要求,如果Content-Length头缺失或指定不正确,它们通常会发送错误响应。在这种情况下,“应该”等同于“必须”。
问题在于(特别是对于保持连接的请求),如果没有Content-Length头,服务器无法知道您的请求消息何时结束。如果您正在流式传输请求实体正文,则另一种选项是发送Transfer-Encoding: chunked头,并手动一次发送一个实体正文块。

总之,如果你想在消息中发送实体主体但不想发送Content-Length头信息,你唯一的选择就是发送分块的HTTP消息。如果你想流式传输实体主体但又无法提前知道其长度,那么这基本上是必须的。

如何对HTTP实体主体进行分块编码以进行流式传输...

Transfer-Encoding: chunked意味着你正在根据RFC2616 Sec3.6.1中规定的约束条件对HTTP消息的实体主体进行编码。这种编码格式可以应用于请求或响应(当然了,它们都是HTTP消息)。这种格式非常有用,因为它允许你在不知道实体主体的大小甚至实体主体具体内容的情况下立即开始发送HTTP消息。事实上,这正是PHP在你没有发送像header('Content-Length: 42')这样的长度头信息时自动透明地为你完成的echo任何输出的操作。

我不会详细介绍分块编码——这是HTTP规范的内容——但如果您想要流传输请求实体主体,您需要像这样做:

<?php

$sock = fsockopen($host,80,$errno, $error);
$readStream = fopen('/some/file/path.txt', 'r+');

fwrite($sock, "POST /somePath HTTP/1.1\r\n" .
              "Host: www.somehost.com\r\n" .
              "Transfer-Encoding: chunked\r\n\r\n");

while (!feof($readStream)) {
    $chunkData = fread($readStream, $chunkSize);
    $chunkLength = strlen($chunkData);
    $chunkData = dechex($chunkLength) . "\r\n$chunkData\r\n";

    fwrite($sock, $chunkData);
}

fwrite($sock, "0\r\n\r\n");

1
我会像这样清理头部代码:$headers = array(); $headers[] = 'POST /somePath HTTP/1.1'; $headers[] = 'Host: example.com'; $headers[] = 'Transfer-Encoding: chunked'; fwrite($sock, implode(PHP_EOL, $headers) . PHP_EOL); - uınbɐɥs
4
很不幸,你的代码在非Windows环境下无法运行。在此情况下使用PHP_EOL会导致问题,因为HTTP协议特别要求头部之间使用CRLF作为换行符。 - user895378
4
很有趣,我不知道那个。无论如何,我的意思是将标题放入数组中,然后将其implode(这样看起来更好看)。 :-) - uınbɐɥs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接