最可靠的PHP流式POST需用fsockopen手动构造HTTP请求,禁用Expect头、采用chunked编码,边生成边fwrite;curl流式需同时满足CURLOPT_READFUNCTION、INFILESIZE设为-1及关闭自动header三个条件。

用 fsockopen 手动发流式 POST 最可靠
PHP 的 file_get_contents 或 curl 默认会把整个请求体缓存进内存再发出,不适合大文件或实时流式上传。真要流式,得绕过封装,直接操作 socket。
核心是:不写完整 body 再 send,而是边生成边 fwrite,并手动构造 HTTP 头(尤其 Content-Length 不能填,得用 Transfer-Encoding: chunked)。
- 必须禁用
Expect: 100-continue,否则服务端可能卡住等确认 - 不要依赖
curl_setopt($ch, CURLOPT_POSTFIELDS, $fp)—— 它只是伪装流式,底层仍读全文件 - 若目标接口要求
Content-Length,那它根本不支持流式,硬上只会 400 或截断
curl 开启流式上传的隐藏条件
curl 确实能流式,但前提是满足三个条件缺一不可:CURLOPT_READFUNCTION + CURLOPT_INFILESIZE 设为 -1 + 关闭自动 header。
curl_setopt($ch, CURLOPT_UPLOAD, true);
curl_setopt($ch, CURLOPT_READFUNCTION, function($ch, $fd, $length) {
return fread($fd, $length); // 每次只读 $length 字节
});
curl_setopt($ch, CURLOPT_INFILESIZE, -1); // 关键:告诉 curl 不预估长度
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Transfer-Encoding: chunked']);
漏掉 CURLOPT_INFILESIZE 设为 -1,curl 会尝试 fstat 取大小,导致 fopen 的流被 rewind 或失败;设成具体数字又违背流式本意。
立即学习“PHP免费学习笔记(深入)”;
流式 POST 下的错误响应难捕获?用 stream_get_contents 抓响应体
流式发送时,curl_exec 返回 false 只代表连接失败,不代表业务失败。HTTP 状态码 200 以外的响应体(比如 413 Payload Too Large 的提示)往往被丢弃。
- 开启
CURLOPT_HEADER并用CURLOPT_HEADERFUNCTION分离状态行 - 响应体必须用
stream_get_contents($http_response_header[0])类似方式读取,不能只信curl_exec返回值 - 如果服务端用 chunked 编码返回响应,PHP 的
fgets会卡在 chunk size 行,得用stream_get_line配合hexdec()解析
PHP 8.1+ 的 StreamWrapper 能简化但不万能
自定义 stream wrapper(实现 stream_open/stream_read)可把任意数据源(数据库游标、加密解密器)伪装成文件流,供 curl 或 fopen 直接读。
但它解决的是「数据从哪来」,不是「怎么发出去」——底层仍依赖 curl 或 socket 的流式能力。而且:
- 所有
stream_*函数默认阻塞,大流量下容易超时 -
php://input在 POST 时只读一次,不能反复 rewind,调试时容易误判 - 某些 SAPI(如 PHP-FPM 的 static mode)会提前关闭 stdin/stdout,导致 wrapper 中的
fgets返回空
真正稳定的流式 POST,永远绕不开对 HTTP 协议分块编码和 socket 写缓冲的控制。别迷信封装,关键字段和时机自己盯住。











