
本文详解 PHP 原生 cURL 实现 PUT 文件上传失败的典型原因——CURLOPT_INFILE 必须传入有效的文件资源流(而非文件路径字符串),并提供完整、可运行的修复方案及最佳实践。
本文详解 php 原生 curl 实现 put 文件上传失败的典型原因——`curlopt_infile` 必须传入有效的文件资源流(而非文件路径字符串),并提供完整、可运行的修复方案及最佳实践。
在 PHP 中使用 curl_setopt() 模拟命令行 curl -X PUT -T file.jpg 上传文件时,一个极易被忽略却致命的错误是:将文件路径字符串直接赋值给 CURLOPT_INFILE。正如官方文档明确指出,CURLOPT_INFILE 的值必须是一个通过 fopen() 打开的有效文件资源(resource),而非字符串路径。否则,cURL 将无法读取数据,导致连接卡在 HTTP/1.1 100 Continue 后停滞、超时,这正是你观察到“TCP 抓包显示请求头发出但无后续数据传输”的根本原因。
此外,CURLOPT_PUT 已被 libcurl 弃用(PHP 文档中亦标注为 deprecated),应统一改用更语义清晰且持续维护的 CURLOPT_UPLOAD => true。
以下是修正后的完整、健壮的上传代码示例:
<?php $url = "http://192.168.1.1:9000/bucket/$fname";
$filePath = "$fname.jpg";
// 验证文件存在且可读
if (!is_readable($filePath)) {
throw new RuntimeException("File not readable: $filePath");
}
$fp = fopen($filePath, 'rb');
if (!$fp) {
throw new RuntimeException("Failed to open file: $filePath");
}
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 2);
curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 建议适当延长(尤其大文件)
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT'); // 显式指定方法,比 CURLOPT_UPLOAD 更直观可控
curl_setopt($curl, CURLOPT_UPLOAD, true); // 启用上传模式(推荐替代 CURLOPT_PUT)
curl_setopt($curl, CURLOPT_INFILE, $fp); // ✅ 关键:必须是 fopen 返回的 resource
curl_setopt($curl, CURLOPT_INFILESIZE, filesize($filePath));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); // 推荐设为 true,便于捕获响应和错误
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
$headers = [
"Host: 192.168.1.1",
"Date: " . date('r'),
"Content-Type: image/jpeg",
"Expect:" // 显式清空 Expect 头,避免 100-continue 协商问题(MinIO 等服务有时对此敏感)
];
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$error = curl_error($curl);
// 清理资源
fclose($fp);
curl_close($curl);
if ($response === false) {
throw new RuntimeException("cURL error: $error (HTTP $httpCode)");
}
if ($httpCode >= 400) {
throw new RuntimeException("HTTP error: $httpCode, Response: " . substr($response, 0, 500));
}
echo "Upload successful! HTTP $httpCode\n";
?>
关键注意事项与最佳实践:
-
✅
CURLOPT_INFILE必须是资源:永远使用fopen($path, 'rb')获取资源,并在 cURL 执行后调用fclose()。切勿传入字符串路径。 -
✅ 优先使用
CURLOPT_CUSTOMREQUEST+CURLOPT_UPLOAD:比已废弃的CURLOPT_PUT更可靠、语义更明确。 -
✅ 显式设置
Expect:空头:可有效规避部分对象存储服务(如 MinIO)对100-continue协商的异常处理,防止挂起。 -
✅ 启用
CURLOPT_RETURNTRANSFER:方便统一处理响应体与错误,避免输出混乱。 -
✅ 严格校验文件状态:上传前检查
is_readable()和filesize(),防止静默失败。 -
⚠️ 避免
exec(curl ...):虽然能工作,但存在安全风险(路径注入)、性能开销大、错误难追踪,原生 cURL 是更优解。
遵循以上规范,即可彻底解决 PHP cURL PUT 上传“卡在 100 Continue”问题,实现与命令行 curl 行为一致的稳定上传。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











