英文:
Presigned S3 upload url expires before expiration time
问题
我有一个用于生成预签名上传URL的Lambda函数。以下是我用于获取URL的代码:
urlExpiresAt := time.Duration(10) * time.Second
s3Session := s3.New(awsSession)
request, _ := s3Session.PutObjectRequest(&s3.PutObjectInput{
Bucket: aws.String(awssettings.FileStorageBucketName),
Key: aws.String(fmt.Sprintf("%s/%s", *cognitoUser.Username, filename)),
ContentType: aws.String(uploadContentType),
})
uploadUrl, err := request.Presign(urlExpiresAt)
在我的前端代码中,我获取该URL并立即尝试发送上传的POST请求:
try {
const {
data: { fileUploadUrl },
} = await graphQlClient.query({
query: gql(getFileUploadUrlQuery),
variables: {
filename,
},
});
const formdata = new FormData();
formdata.append("file", file);
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);
ajaxRequest.open("POST", fileUploadUrl);
ajaxRequest.send(formdata);
} catch (err) {
console.log(err);
}
为了测试目的,我将我的存储桶配置为公开可访问:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "UploadFiles",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "<bucketARN>/*"
}
]
}
为了测试目的,CORS配置如下:
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"POST"
],
"AllowedOrigins": [
"http://localhost:8080"
],
"ExposeHeaders": []
}
]
CORS预检请求返回状态码200
,但发送文件内容的POST
请求被拒绝,状态码为403
,并显示以下错误:
<Error>
<Code>AccessDenied</Code>
<Message>Request has expired</Message>
<X-Amz-Expires>5</X-Amz-Expires>
<Expires>2022-09-07T04:14:50Z</Expires>
<ServerTime>2022-09-13T06:40:18Z</ServerTime>
...
</Error>
根据这个错误信息,看起来URL在预签名之前就已经过期了,这是不合理的。
我在这里做错了什么?
英文:
I have a lambda to generate pre-signed upload URL. Here is the code I use to get URL:
urlExpiresAt := time.Duration(10) * time.Second
s3Session := s3.New(awsSession)
request, _ := s3Session.PutObjectRequest(&s3.PutObjectInput{
Bucket: aws.String(awssettings.FileStorageBucketName),
Key: aws.String(fmt.Sprintf("%s/%s", *cognitoUser.Username, filename)),
ContentType: aws.String(uploadContentType),
})
uploadUrl, err := request.Presign(urlExpiresAt)
On my front-end I retrieve that URL and immediately try to send upload POST request to it:
try {
const {
data: { fileUploadUrl },
} = await graphQlClient.query({
query: gql(getFileUploadUrlQuery),
variables: {
filename,
},
});
const formdata = new FormData();
formdata.append("file", file);
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);
ajaxRequest.open("POST", fileUploadUrl);
ajaxRequest.send(formdata);
} catch (err) {
console.log(err);
}
for testing purposes I configured my bucket to be publically accessable:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "UploadFiles",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "<bucketARN>/*"
}
]
}
and CORS for testing purposes is configured like this:
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"POST"
],
"AllowedOrigins": [
"http://localhost:8080"
],
"ExposeHeaders": []
}
]
CORS pre-flight request returns status 200
, however POST
request to send file content gets rejected with status 403
and following error:
<Error>
<Code>AccessDenied</Code>
<Message>Request has expired</Message>
<X-Amz-Expires>5</X-Amz-Expires>
<Expires>2022-09-07T04:14:50Z</Expires>
<ServerTime>2022-09-13T06:40:18Z</ServerTime>
...
</Error>
reading this error it looks like URL has expired before it was pre-signed which doesn't make sense.
What am I doing wrong here?
答案1
得分: 0
我发现我的解决方案有两个问题:
首先,生成的预签名URL使用的是PUT
方法,而不是POST
。
其次,我使用的GraphQL客户端AWSAppSyncClient
默认缓存查询结果,所以我只需要通过fetchPolicy: "network-only"
禁用缓存。
const {
data: { fileUploadUrl },
} = await graphQlClient.query({
query: gql(getFileUploadUrlQuery),
fetchPolicy: "network-only", // <- 这一行
variables: {
filename,
},
});
英文:
I figured out there was 2 issues with my solution:
First, presigned URL generated to use PUT
method, not POST
Second, AWSAppSyncClient
I use as a GraphQL client caches query results by default so all I needed is to disable caching with fetchPolicy: "network-only"
const {
data: { fileUploadUrl },
} = await graphQlClient.query({
query: gql(getFileUploadUrlQuery),
fetchPolicy: "network-only", // <- THIS LINE
variables: {
filename,
},
});
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论