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

使用Perl与WWW::Curl::Easy上传Amazon S3 Bucket失败

上传JPEG到Amazon S3时SignatureDoesNotMatch错误排查与解决

问题场景

尝试用Perl代码上传JPEG文件到S3 Bucket时收到SignatureDoesNotMatch错误,但使用相同URL和请求头的终端curl命令能成功上传。

Perl代码实现

my $curl =  WWW::Curl::Easy->new();

$curl_headers->[0] = sprintf 'Content-Type:%s', $extra_headers->{'Content-Type'};
$curl_headers->[1] = sprintf 'Content-MD5:%s', $extra_headers->{'Content-MD5'};
$curl_headers->[2] = sprintf 'Content-Disposition:%s', $extra_headers->{'Content-Disposition'};
$curl->setopt(CURLOPT_HEADER,       1);
$curl->setopt(CURLOPT_NOPROGRESS,   1);
$curl->setopt(CURLOPT_URL, $url);
$curl->setopt(CURLOPT_HTTPHEADER, $curl_headers);
$curl->setopt(CURLOPT_CUSTOMREQUEST,  'PUT');
$curl->setopt(CURLOPT_POSTFIELDSIZE_LARGE, $size + 0);
$curl->setopt(CURLOPT_READDATA, \$image_fh); # cgi temp file file handle
$curl->perform();

错误信息

403 Forbidden
403 Forbidden
.............
Server: AmazonS3

<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided.

成功的curl命令

curl -X PUT \
-H "Content-Type:image/jpeg" \
-H "Content-MD5:file_checksum" \
-H "Content-Disposition:inline; filename=\"Tesla-Model-3-600x350-1.jpeg\"; filename*=UTF-8''Tesla-Model-3-600x350-1.jpeg" \
-T Tesla-Model-3-600x350-1.jpeg \
URL

可能的原因

  • 请求头格式不一致:Perl代码构建的请求头可能存在空格、引号转义或编码差异,比如Content-Disposition的引号处理,导致签名计算时的请求头内容与S3预期不符。
  • 文件读取方式错误:Perl中传入\$image_fh作为CURLOPT_READDATA的值,和curl命令-T的文件读取逻辑不同,可能导致上传内容字节数或内容本身有差异,影响MD5校验和签名计算。
  • 文件大小参数错误:$size + 0的写法可能引发类型转换问题,导致声明的文件大小与实际上传字节数不一致,触发S3的签名校验失败。
  • PUT请求处理逻辑差异:使用CURLOPT_CUSTOMREQUEST设置PUT,和curl内置的PUT上传逻辑(-T触发)不一致,可能缺失自动处理的请求头(如Content-Length)或请求细节。

解决方法

  1. 统一请求头内容
    检查Content-Disposition等字段的字符串是否和curl命令完全一致,比如curl中的filename=\"xxx\"在Perl双引号字符串中应直接写为filename="xxx",避免额外转义导致内容差异。

  2. 修正文件句柄传递方式
    CURLOPT_READDATA需直接传入文件句柄,而非引用,同时确保文件指针处于文件开头:

    $curl->setopt(CURLOPT_READDATA, $image_fh);
    
  3. 验证文件大小准确性
    使用可靠方式获取文件实际字节数,比如CGI临时文件的size属性或-s函数,避免$size + 0可能的类型错误:

    my $size = -s $image_fh; # 需确保文件句柄关联的路径可被-s识别
    
  4. 改用标准PUT上传配置
    移除CURLOPT_CUSTOMREQUEST,改用curl专用的上传选项,和-T行为保持一致:

    $curl->setopt(CURLOPT_UPLOAD, 1);
    
  5. 确保Content-Length正确性
    可手动添加Content-Length请求头,或依赖curl自动处理,确保值与文件实际大小一致:

    push @$curl_headers, "Content-Length: $size";
    
  6. 对比请求细节
    开启curl调试模式,输出完整请求内容,和终端curl的请求对比,定位差异:

    $curl->setopt(CURLOPT_VERBOSE, 1);
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 15:18:57