由于 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 失败的主要内容,如果未能解决你的问题,请参考以下文章

识别由于 CORS 问题导致的提取错误

由于 CORS 问题,firebase 托管阻止脚本

由于 CORS 限制,无法使用 firebase 在本地进行测试

由于 CORS 问题,无法访问 API

由于 CORS,无法从 S3 存储桶中获取图像

由于 CORS 问题,无法在 Angular 中发出 HttpClient 发布请求