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

S3预签名上传URL未到过期时间却提示已过期问题排查

问题分析与解决方案

从错误信息来看,预签名URL的过期时间(2022-09-07T04:14:50Z)远早于S3服务器当前时间(2022-09-13T06:40:18Z),同时你设置的有效期是10秒,但错误中X-Amz-Expires显示为5秒,这指向几个明确的问题:

核心原因1:Lambda运行环境时间与AWS S3服务器时间严重不同步

预签名URL的有效期是基于生成时的Lambda本地时间计算的,如果Lambda的系统时间比S3服务器时间慢了数天,那么生成的URL在S3看来从一开始就已经过期。

解决方法:

  • 在Lambda代码中添加日志,打印当前时间:
    fmt.Println("Lambda current time:", time.Now().UTC())
    
    对比S3返回的ServerTime,确认时间是否同步。AWS Lambda默认运行环境时间是UTC且与NTP同步,若你使用了自定义运行时或修改过时间配置,需修复时间同步问题。

核心原因2:预签名请求方法与前端上传方法不匹配

你通过PutObjectRequest生成的是PUT请求的预签名URL,但前端使用POST请求+FormData上传,这会导致S3的签名验证失败,可能表现为"请求过期"这类误导性错误。

解决方法:

方案A:修改前端使用PUT方法上传

将前端代码中的请求方法改为PUT,直接发送文件内容而非FormData:

try {
  const {
    data: { fileUploadUrl },
  } = await graphQlClient.query({
    query: gql(getFileUploadUrlQuery),
    variables: { filename },
  });

  const ajaxRequest = new XMLHttpRequest();
  ajaxRequest.upload.addEventListener("progress", progressHandler, false);
  ajaxRequest.addEventListener("load", completeHandler, false);
  ajaxRequest.addEventListener("error", errorHandler, false);
  ajaxRequest.addEventListener("abort", abortHandler, false);
  // 改为PUT方法
  ajaxRequest.open("PUT", fileUploadUrl);
  // 设置Content-Type与预签名时一致
  ajaxRequest.setRequestHeader("Content-Type", file.type);
  // 直接发送文件,而非FormData
  ajaxRequest.send(file);
} catch (err) {
  console.log(err);
}

方案B:后端生成POST表单的预签名

如果你需要保留POST+FormData的上传方式,需改用S3的POST上传预签名机制,示例代码如下:

// 生成POST上传表单参数
postInput := &s3.PostPolicyInput{
  Bucket: aws.String(awssettings.FileStorageBucketName),
  Expires: aws.Time(time.Now().Add(10 * time.Second)),
  Conditions: [][]interface{}{
    []interface{}{"eq", "$key", fmt.Sprintf("%s/%s", *cognitoUser.Username, filename)},
    []interface{}{"eq", "$Content-Type", uploadContentType},
    []interface{}{"content-length-range", 0, 10485760}, // 限制文件大小,按需调整
  },
}

postPolicy, err := s3Session.PostPolicy(postInput)
if err != nil {
  // 处理错误
}
// 获取表单字段和POST URL
formFields := postPolicy.FormFields
postUrl := fmt.Sprintf("https://%s.s3.amazonaws.com", awssettings.FileStorageBucketName)

前端需使用这些表单字段构建FormData,再发送POST请求到postUrl。

额外检查点

  1. 确认urlExpiresAt设置是否生效:在Lambda中打印该值,以及计算出的过期时间:

    fmt.Println("Expires duration:", urlExpiresAt)
    fmt.Println("Calculated expires time:", time.Now().Add(urlExpiresAt).UTC())
    

    若输出的过期时间与错误中的Expires字段不符,检查是否有其他代码逻辑修改了有效期设置。

  2. 移除不必要的公开存储桶策略:预签名URL本身已包含上传权限,无需设置Principal: "*"的公开策略,该策略可能引发权限混淆,建议暂时移除,仅保留预签名机制验证上传。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 19:45:34