You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PHP CURL上传文件至AWS S3返回400错误求排查

问题描述

补充说明
我调试PHP和Golang代码时碰到大麻烦,但直接发Curl请求能解决问题:

curl -v -F $Signature -F $Date -F $Credential -F $Key -F $Algo -F $Policy -F $File $Url

我试着把这个请求转成PHP代码,但一直不行,现在只能用shell_exec()来执行这个请求。
补充说明结束

我的服务商没给AWS S3访问密钥,只允许通过AWS规定的表单上传文件,我想实现程序自动化上传。这个表单能正常使用,但用PHP CURL模拟请求时一直返回400错误。我已经删掉AWS示例里不必要的部分,尽量让CURL请求和表单一致,有人能指出问题在哪吗?


可用的HTML表单

<form action="https://<CLIENT NAME>.s3.amazonaws.com/" method="post" enctype="multipart/form-data">
    <input type="input"  name="key" value="something.pdf" /><br />
    <input type="text"   name="X-Amz-Credential" value="<SOME key>/20221017/REGION/s3/aws4_request" />
    <input type="text"   name="X-Amz-Algorithm" value="AWS4-HMAC-SHA256" />
    <input type="text"   name="X-Amz-Date" value="20221017T162325Z" />
    <input type="hidden" name="X-Amz-Signature" value='<SIGNATURE>' />
    <input type="hidden" name="Policy" value="<POLICY>" />
    <input type="file"   name="file" /> <br />
    <input type="submit" name="submit" value="Upload to Amazon S3" />
  </form>

我尝试的PHP-Curl示例代码

<?php
if (isset($_POST['btnUpload'])) {
    $url = "https://<SERVICE PROVIDER>.s3.amazonaws.com/";
    $filename = $_FILES['file']['name'];
    $filedata = $_FILES['file']['tmp_name'];
    $filesize = $_FILES['file']['size'];
    if ($filedata != '') {
        $headers = array(
            "enctype: multipart/form-data", 
            'Accept-Language: en-US,en;q=0.5',
            'Connection: keep-alive',
            'Upgrade-Insecure-Requests: 1',
            "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
            "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7",
            "Keep-Alive: 300",
            "Connection: keep-alive"
        ); 

        
        $postfields = array(
            "file" => "", 
            "x-amz-signature" => "<SIGNATURE>", 
            "x-amz-date" => "20221017T173731Z", 
            "x-amz-credential" => "<EY>/20221017/ap-south-1/s3/aws4_request",
            "key" => "<SAMPLE.FILE>", 
            "policy" => "<POLICY>",
            "x-amz-algorithm" => "AWS4-HMAC-SHA256"
        );

        $ch = curl_init();
        $options = array(
            CURLOPT_URL => $url,
            CURLOPT_HEADER => true,
            CURLOPT_POST => 1,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_POSTFIELDS => $postfields,
            CURLOPT_INFILESIZE => $filesize,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLINFO_HEADER_OUT => true,
            CURLOPT_USERAGENT=>'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.42',
            CURLOPT_ENCODING=>'gzip, deflate, br'
        ); // cURL options

        curl_setopt_array($ch, $options);
        curl_exec($ch);

        if(!curl_errno($ch)) {
           
            $info = curl_getinfo($ch);
            if ($info['http_code'] == 200)
                $errmsg = "File uploaded successfully";
        } else {
           
            $errmsg = curl_error($ch);
        }
        $info = curl_getinfo($ch);
        print_r($info);
        curl_close($ch);
    }
}


?>
<form action="" method="post" name="frmUpload" enctype="multipart/form-data">
<tr>
  <td>Upload</td>
  <td align="center">:</td>
  <td><input name="file" type="file" id="file"/></td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td align="center">&nbsp;</td>
  <td><input name="btnUpload" type="submit" value="Upload" /></td>
</tr>

问题分析与修复方案

你的PHP CURL代码存在这些核心问题:

  • 文件字段未正确传递:$postfields里把"file"设为空字符串,完全没把上传的文件传给S3。必须用CURLFile类指定文件路径、MIME类型和文件名,PHP才会正确生成多部分表单的文件上传格式。
  • 参数大小写不匹配:HTML表单里的参数名是X-Amz-Credential、X-Amz-Date这种驼峰格式,但PHP里用了全小写的x-amz-credential、x-amz-date。AWS S3的表单签名严格匹配参数名大小写,必须和表单完全一致。
  • 手动设置enctype头部多余且错误:当CURLOPT_POSTFIELDS传入数组时,PHP会自动生成带边界符的multipart/form-data头部,手动添加会覆盖自动生成的内容,导致S3无法解析表单。
  • 重复的Connection头部:$headers里同时存在'Connection: keep-alive'和"Keep-Alive: 300",重复设置可能引发请求异常。

修改后的PHP代码示例

<?php
if (isset($_POST['btnUpload'])) {
    $url = "https://<SERVICE PROVIDER>.s3.amazonaws.com/";
    $filename = $_FILES['file']['name'];
    $filedata = $_FILES['file']['tmp_name'];
    $filetype = $_FILES['file']['type'];
    
    if ($filedata != '' && file_exists($filedata)) {
        // 只保留必要的请求头部
        $headers = array(
            'Accept-Language: en-US,en;q=0.5',
            'Upgrade-Insecure-Requests: 1',
            "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
            "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7"
        ); 

        $postfields = array(
            // 正确传递文件
            "file" => new CURLFile($filedata, $filetype, $filename),
            // 保持参数名和HTML表单完全一致的大小写
            "X-Amz-Signature" => "<SIGNATURE>", 
            "X-Amz-Date" => "20221017T173731Z", 
            "X-Amz-Credential" => "<EY>/20221017/ap-south-1/s3/aws4_request",
            "key" => "<SAMPLE.FILE>", 
            "Policy" => "<POLICY>",
            "X-Amz-Algorithm" => "AWS4-HMAC-SHA256"
        );

        $ch = curl_init();
        $options = array(
            CURLOPT_URL => $url,
            CURLOPT_HEADER => true,
            CURLOPT_POST => 1,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_POSTFIELDS => $postfields,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLINFO_HEADER_OUT => true,
            CURLOPT_USERAGENT=>'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.42',
            CURLOPT_ENCODING=>'gzip, deflate, br'
        );

        curl_setopt_array($ch, $options);
        $response = curl_exec($ch);

        if(!curl_errno($ch)) {
            $info = curl_getinfo($ch);
            if ($info['http_code'] == 200) {
                $errmsg = "文件上传成功";
            } else {
                $errmsg = "请求失败,状态码:" . $info['http_code'];
                // 打印S3返回的具体错误信息,方便排查
                echo "<pre>" . $response . "</pre>";
            }
        } else {
            $errmsg = curl_error($ch);
        }
        
        echo $errmsg;
        print_r($info);
        curl_close($ch);
    }
}
?>
<form action="" method="post" name="frmUpload" enctype="multipart/form-data">
<table>
<tr>
  <td>上传文件</td>
  <td align="center">:</td>
  <td><input name="file" type="file" id="file"/></td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td align="center">&nbsp;</td>
  <td><input name="btnUpload" type="submit" value="上传" /></td>
</tr>
</table>
</form>

内容的提问来源于stack exchange,提问作者Chilarai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 07:40:21