cURL 多文件上传必须为每个文件创建独立 CURLFile 实例并用 realpath() 确保路径有效,字段名需与后端嵌套结构严格一致,禁用 @ 前缀,显式设置 CURLOPT_SAFE_UPLOAD 和 SSL 选项,避免手动设置 Content-Type,并注意 PHP 服务端 upload_max_filesize 等限制。
PHP 的 curl_setopt 不支持把多个 fopen() 句柄塞进一个 CURLOPT_POSTFIELDS 数组里直接发——它会报 Array to string conversion 或静默失败。必须为每个文件单独构造 CURLFile 实例,并确保路径真实可读。
new CURLFile(realpath($path)) 包裹每个文件,别用相对路径或未 realpath() 的字符串files[],你就得写 'files[]' => $curlFile
@ 前缀写法(已废弃),否则 PHP 7.4+ 会警告,8.0+ 直接报错CURLOPT_SAFE_UPLOAD
PHP 5.6+ 默认开启 CURLOPT_SAFE_UPLOAD(值为 true),但这个选项只控制是否允许旧式 @/path 语法,**不影响 CURLFile**。不过很多老项目还开着 ini_set('curl.cainfo', ...) 或混用旧逻辑,漏设会导致 SSL 验证失败或上传被截断。
curl_setopt($ch, CURLOPT_SAFE_UPLOAD, true),哪怕它本就是默认值curl_setopt($ch, CURLOPT_SSL_VE
RIFYPEER, false)(仅调试)或正确配置 CA 证书路径curl_multi_init() 并发多文件,每个 handle 都要单独设这些选项,不能复用$_FILES 接收不到?检查 enctype 和字段命名嵌套后端 PHP 脚本看到空 $_FILES,大概率不是 cURL 问题,而是表单编码类型或字段名不匹配。浏览器表单用 enctype="multipart/form-data" 是硬性要求,cURL 没有这个属性,但它靠 CURLOPT_POSTFIELDS 中含 CURLFile 自动触发 multipart 编码——前提是没手动设 Content-Type 头。
Content-Type: multipart/form-data; boundary=xxx,curl 会自己生成并管理 boundarydata[files][] 这种嵌套键名,cURL 必须传 'data[files][]' => new CURLFile(...),不能只传 'files[]'
curl_getinfo($ch, CURLINFO_CONTENT_TYPE) 看发出的请求头,确认是否含 multipart/form-data 及有效 boundaryupload_max_filesize 或超时cURL 本身不限文件大小,但 PHP 的 upload_max_filesize、post_max_size、max_execution_time 三者都会中途掐断。尤其并发上传时,max_execution_time 是所有请求共享的,不是每个 curl handle 独立计时。
upload_max_filesize = 128M、post_max_size = 130M(略大于前者)curl_setopt($ch, CURLOPT_TIMEOUT, 300) 和 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30)
filesize($path) > 2 * 1024 * 1024 做简单校验,避免传到一半才发现后端拒收实际最易忽略的是字段名嵌套层级和 realpath() 缺失——这两个点错一个,$_FILES 就全空,且无明确报错。