由于 CORS,使用 createPresignedPost 或 getPresignedUrl 将 formData 文件从客户端上传到 S3 失败

Posted

技术标签:

【中文标题】由于 CORS,使用 createPresignedPost 或 getPresignedUrl 将 formData 文件从客户端上传到 S3 失败【英文标题】:Uploading formData file from client to S3 using createPresignedPost or getPresignedUrl fails due to CORS 【发布时间】:2021-05-03 22:12:21 【问题描述】:

我正在使用 React Web 应用程序并尝试将文件上传到 AWS S3。 我在本地 (localhost:3000) 和部署到生产环境(Vercel 无服务器功能)时都尝试了所有方法。

我首先使用 fetch 来检索文件上传的预签名 url,效果很好:

module.exports = async (req, res) => 
    let fileName, fileType = req.body;
    const post = await s3.createPresignedPost(
        Bucket: BUCKET_NAME,
        Fields: 
            key: fileName
        ,
        ContentType: fileType,
        Expires: 60
    );
    res.status(200).json(post);

然后,在第二次获取中:

await fetch(url, method: 'POST', body: formData

我明白了:

fetch has been blocked by CORS policy: No 'Access-Control-Allow-Origin'

我已经在存储桶配置中正确设置了 Permissions -> CORS json:

[
    
        "AllowedHeaders": [
            "*",
            "Content-*"
        ],
        "AllowedMethods": [
            "HEAD",
            "GET",
            "PUT",
            "POST",
            "DELETE"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": [
            "*"
        ]
    
]

我还在我的代码中验证了存储桶名称。

我试过了:

getPresignedUrl & createPresignedPost 都可以生成网址 PUTPOST 都请求 在提取请求中设置标头(Content-Typex-amz-acl) 使用不同的CORS jsons,允许所有来源,暴露不同的头文件,允许所有方法(包括HEAD,允许不同的头文件(如"Content-*") 为不同的用户配置不同的权限(特别是我需要的,所有 S3 权限等) Settings bucket policy 每次尝试前清除 Chrome 的缓存(使用硬重载)或通过 code 进行

我已经尝试了所有方法并阅读了数十篇文章(1、2)并在此处浏览问题 - 但它仍然无法正常工作。不在本地,也不在生产中。

【问题讨论】:

我找不到具体问题。但是,我可以提供一个我刚刚尝试过的可行解决方案作为答案。 【参考方案1】:

经过无数小时的研究,我找到了问题所在。 CORS 错误的原因是我没有在初始 AWS.config.update 配置设置中包含 region

一旦我添加了region 键,CORS 错误就解决了:

const AWS = require('aws-sdk');

AWS.config.update(
    accessKeyId: 'XXXXX',
    secretAccessKey: 'XXXXXX',
    region: 'us-west-2', // Must be the same as your bucket
);

const s3Client = new AWS.S3();

未配置存储桶区域返回的错误是CORS,真是令人困惑和误导。

【讨论】:

【参考方案2】:

确保文件名与Key完全匹配

const bucketParms = 
  Bucket: "sample-temp-bucket",
  Key: "HeadShot.jpg",
  Expires: 3600,
;

s3.getSignedUrl("putObject", bucketParms, (error, url) => 
  if (error) console.log("error", error);
  if (url) console.log("url", url);
);

这里我们可以测试postman or curl生成的url

然后我们可以进行 http PUT 调用。

this.http
  .put(
    "https://sample-temp-bucket.s3.amazonaws.com/HeadShot.jpg?X-Amz-Algorithm=AWS4-HMAC....",
    formData
  )
  .subscribe((response) => 
    console.log("response received is ", response);
  );

而 CORS,ExposeHeaders 的通配符不接受(至少来自控制台),它只使用空数组 "ExposeHeaders": []

【讨论】:

验证了所有这些东西,但我仍然遇到同样的错误:Access to fetch at 'https://s3.amazonaws.com/bucket-name' from origin 'http://localhost:3000' 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. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. FileUpload.jsx:34 POST https://s3.amazonaws.com/bucket-name net::ERR_FAILED

以上是关于由于 CORS,使用 createPresignedPost 或 getPresignedUrl 将 formData 文件从客户端上传到 S3 失败的主要内容,如果未能解决你的问题,请参考以下文章