使用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)或请求细节。
解决方法
统一请求头内容
检查Content-Disposition等字段的字符串是否和curl命令完全一致,比如curl中的filename=\"xxx\"在Perl双引号字符串中应直接写为filename="xxx",避免额外转义导致内容差异。修正文件句柄传递方式
CURLOPT_READDATA需直接传入文件句柄,而非引用,同时确保文件指针处于文件开头:$curl->setopt(CURLOPT_READDATA, $image_fh);验证文件大小准确性
使用可靠方式获取文件实际字节数,比如CGI临时文件的size属性或-s函数,避免$size + 0可能的类型错误:my $size = -s $image_fh; # 需确保文件句柄关联的路径可被-s识别改用标准PUT上传配置
移除CURLOPT_CUSTOMREQUEST,改用curl专用的上传选项,和-T行为保持一致:$curl->setopt(CURLOPT_UPLOAD, 1);确保Content-Length正确性
可手动添加Content-Length请求头,或依赖curl自动处理,确保值与文件实际大小一致:push @$curl_headers, "Content-Length: $size";对比请求细节
开启curl调试模式,输出完整请求内容,和终端curl的请求对比,定位差异:$curl->setopt(CURLOPT_VERBOSE, 1);
内容的提问来源于stack exchange,提问作者Dragos Trif
相关产品推荐
相关产品推荐

