AWS S3使用PHP生成带UploadId、PartNumber的分片上传预签名URL及报错排查
问题根因说明
- 核心操作选择错误:分片上传的单分片提交(对应AWS的
UploadPart接口)是完全独立于普通单文件上传(PutObject)的操作。你之前用PutObject构建签名命令时,SDK的操作定义里根本没有UploadId、PartNumber这两个参数的字段,构建命令的过程中会直接把这两个非schema内的参数过滤掉,既不会纳入签名计算,也不会拼到最终的预签名URL上。 - 之前的URL能发起请求但关联不到分片的原因:用
PutObject生成的URL触发的是普通单文件上传链路,你传的分片内容会被直接当成普通单文件写到目标Key上,整个过程和你之前初始化的分片上传任务没有任何关联。后续合并分片时,自然会因为找不到任务关联的分片报InvalidPart错误。 - 文档查找遗漏的原因:JS SDK的
getSignedUrl是通用的签名工具,本身不定义支持哪些参数。在通用签名说明页找不到UploadId、PartNumber的相关说明是正常的——这些参数的定义是属于UploadPart这个具体API操作的,不是签名工具本身的。JS代码里传入的操作名uploadPart,会触发SDK加载UploadPart操作的参数schema,自动把这两个参数纳入签名流程。
正确的PHP实现代码
// 基于AWS官方PHP SDK实现,与参考的JS版逻辑完全对齐 $command = $s3Client->getCommand('UploadPart', [ 'Bucket' => '你的目标Bucket名称', 'Key' => '上传目标的对象Key', 'UploadId' => $uploadId, // 初始化分片上传接口返回的UploadId 'PartNumber' => $partNumber, // 从1开始计数的分片序号 ]); // 生成有效期5分钟的预签名URL,和JS代码的过期时间逻辑对齐 $presignedRequest = $s3Client->createPresignedRequest($command, '+5 minutes'); $uploadUrl = (string)$presignedRequest->getUri();
关键注意事项
- 不要在构建
UploadPart命令时传入Body参数:JS代码里传空Body是JS SDK的兼容写法,PHP SDK中传入Body会触发预签名阶段的内容CRC校验,导致客户端实际上传分片时因为签名值和实际内容不匹配被拒绝。 - 生成的URL不需要手动拼接
UploadId、PartNumber参数:SDK构建UploadPart命令时会自动将这两个参数作为查询参数拼到URL上,同时把这些参数值纳入签名计算,避免参数被篡改。 - 客户端上传分片时,直接用PUT方法请求生成的URL,将分片二进制内容作为请求体发送即可。收到响应后要把返回的ETag和对应
PartNumber按顺序保存,后续调用CompleteMultipartUpload时提交这些值,就不会触发InvalidPart错误。
内容的提问来源于stack exchange,提问作者Josh
相关产品推荐
相关产品推荐

