javascript - 通过浏览器将对象放在 AWS S3 上时出现意外错误 (putObject)
问题描述
我通过我的 Node/Express 后端成功获得了一个预签名 URL,用于putObject
S3 上的请求,并尝试通过浏览器上传相关文件。
当我put
通过浏览器(或邮递员)发出请求时,我得到一个400
or 403
。我是 S3 的新手,不知道在哪里可以查找有关此的信息。
我知道如何正确获取预签名的 URL,但为什么我put
对文件的请求是我得到的 URL 失败?
据我所知,我收到请求的存储桶是可公开访问的。
我查看了不同的代码演练教程、有关 S3 的文档以及此处的帖子。
我在前端请求预签名 URL 的代码
// Returns null or array of objects of type { url : string | null; contentType: string; }
const filesDescription = fileList[+key];
if (filesDescription === undefined || filesDescription === null) continue;
const Key = `${prefix}/${filesDescription.name}`;
const ContentType = filesDescription.type;
const request = generateEndpointConfig(APIEndpoints.GET_PRESIGNED_URL, { Key, ContentType, userID });
const res = await apiCall(request.METHOD, request.URL, request.DATA);
const url = typeof res.data.signedUrl === "string" ? res.data.signedUrl : null;
presignedUrls.push({url, contentType: ContentType});
我的 Node/Express 后端获取 URL 的代码
const { ContentType, Key } = req.body;
const s3 = new S3({
accessKeyId: AWS_ACCESS_ID,
secretAccessKey: AWS_SECRET
});
const url = await s3.getSignedUrlPromise("putObject",
{
Bucket: AMAZON_AWS_STATIC_BUCKET,
ContentType,
Key,
Expires: 300
});
return res.status(StatusCodes.OK).json({ signedUrl: url })
最后,put
我提出的上传文件的请求
const presignedUrls = await getPresignedUrls(+idx);
if (presignedUrls === null) return false;
for (const fileIdx in presignedUrls)
{
const fileList = files[idx];
const uploadConfig = presignedUrls[fileIdx];
if (fileList === null || uploadConfig.url === null) continue;
const fileToUpload = fileList[fileIdx];
try
{
// Make put request for corresponding file to cloud service
await axios.put(uploadConfig.url, fileToUpload,
{
headers:
{
"Content-Type": uploadConfig.contentType
}
});
}
catch(err)
{
return false;
}
我还应该提到,因为我是为经过身份验证的用户执行此操作,所以我还传输Authorization
了表单的标题Bearer <TOKEN>
。
=== 编辑 ===
这是我在Postman中返回状态码 403 的示例错误响应。
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
<RequestId>...</RequestId>
<HostId>...</HostId>
</Error>
=== 编辑 2 ===
我的存储桶上的政策是
{
"Version": "2012-10-17",
"Id": "<POLICY_ID>",
"Statement": [
{
"Sid": "<STMT_ID>",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <CLOUDFRONT_ORIGIN_ID>"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<S3_BUCKET_NAME>/*"
},
{
"Sid": "<STMT_ID>",
"Effect": "Allow",
"Principal": {
"Federated": [
"http://localhost:3000",
"https://www.my-website.com/"
],
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::<S3_BUCKET_NAME>/*"
}
]
}
我存储桶上的 CORS 配置是
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"PUT",
"POST",
"DELETE"
],
"AllowedOrigins": [
"https://www.my-website.com",
"http://localhost:3000"
],
"ExposeHeaders": []
}
]
作为参考,此存储桶为 CDN 网络提供 CloudFront 分配,并且该存储桶仅在 GET 请求进入域级别时受到限制。
=== 编辑 3 ===
这是我尝试在浏览器上上传时返回的错误。
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
<AWSAccessKeyId>...</AWSAccessKeyId>
<StringToSign>GET [A_LONG_NUMBER] /[BUCKET_NAME]/[PREFIX]/[IMAGE_NAME].jpg</StringToSign>
<SignatureProvided>...</SignatureProvided>
<StringToSignBytes>[SOME_BYTES]</StringToSignBytes>
<RequestId>...</RequestId>
<HostId>...</HostId>
</Error>
=== 最终编辑 ===
除了@jarmod 的回答之外,问题是Authorization
一旦站点上的用户通过身份验证,我的应用程序配置就会自动将标头转发给所有请求。删除调用 S3 的此标头使该过程在浏览器上成功运行。
解决方案
您的 S3 存储桶上有 CORS 配置设置吗?这指定了哪些源可以访问您的存储桶,以及它们可以使用哪些 HTTP 方法。在这种情况下,您通常会在开发者控制台中看到如下所示的错误Access to fetch at 'YOUR-PRESIGNED-URL' from origin 'YOUR-ORIGIN' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource
此页面介绍了基本配置:https ://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html
推荐阅读
- python - 当文件名具有不同长度的数字时,如何使用 pathlib.glob() 遍历文件
- lucene - 为什么 Lucene MatchAllDocsQuery "*:* AND name:a" 和休眠搜索会提供错误的结果?
- python - 如何在猜字游戏的代码中让时钟在 while 循环之上运行?
- azure - Active Directory 管理工具 - 用户无法登录
- javascript - 使用反应表中的列渲染表后页面显示空白?
- java - Android Room:获取当前数据库版本/检查是否过时
- python - 如何使用 matplotlib 绘制带有 (x, y, z, r, g, b) 的 3d 图像?
- javascript - 如何使用带有回调函数的jQuery?
- php - PHP根据另一个数组对数组进行排序
- python - 如何在 CSV 文件中写入单词及其向量?