如何在 Heroku 上为 AWS S3 正确设置签名 url 上传?

How to correctly setup signed url uploads for AWS S3 on Heroku?

问题

我在 Heroku 上有一个简单的应用程序 运行,用户可以在其中上传和查看 images/videos。我正在尝试使用签名 url。我试过了this tutorial and this one。事实上,我克隆了 Github 仓库。问题是每当我尝试上传图片时,我都会收到这些错误:

[错误] 预检响应不成功

[错误] 由于访问控制检查,无法加载 XMLHttpRequest(长 link)。

[错误] 加载资源失败:预检响应不成功(img.jp2,第 0 行)

具体问题

我认为问题可能出在 S3 存储桶设置上,因为我对 AWS 的使用经验不多。所以我的问题是是什么导致了这个问题,我该如何解决?

这是我的 COR 配置

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>HEAD</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

这是生成预签名 url

的代码
aws.config.region = 'us-west-1';
BUCKET = process.env.S3_BUCKET;

app.get('/account', (req, res) => res.render('account.html'));

app.get('/sign-s3', (req, res) => {
  const s3 = new aws.S3();
  const fileName = req.query['file-name'];
  const fileType = req.query['file-type'];
  const s3Params = {
    Bucket: S3_BUCKET,
    Key: fileName,
    Expires: 60,
    ContentType: fileType,
    ACL: 'public-read'
  };

  s3.getSignedUrl('putObject', s3Params, (err, data) => {
    if(err){
      console.log(err);
      return res.end();
    }
    const returnData = {
      signedRequest: data,
      url: `https://${S3_BUCKET}.s3.amazonaws.com/${fileName}`
    };
    res.write(JSON.stringify(returnData));
    res.end();
  });
});

这是尝试上传到 S3 的代码

function uploadFile(file, signedRequest, url){
  const xhr = new XMLHttpRequest();
  xhr.open('PUT', signedRequest);
  xhr.onreadystatechange = () => {
    if(xhr.readyState === 4){
      if(xhr.status === 200){
        alert('success')
      }
      else{
        alert('Could not upload file.');
      }
    }
  };
  xhr.send(file);
}

什么都试过了

添加

<AllowedMethod>OPTIONS</AllowedMethod>

结果

Found unsupported HTTP method in CORS config. Unsupported method is OPTIONS

补充说明

我已经尝试过 chrome 和 safari。原来我不小心默认使用

 <AllowedHeader>authorization</AllowedHeader>

改回

 <AllowedHeader>*</AllowedHeader>

结果:

[Long link] Failed to load resource: the server responded with a status of 403 (Forbidden)

我想我发现了问题,你应该设置 content-type 因为它在生成签名时也会被使用。

xhr.setRequestHeader("Content-Type", "application/octet-stream");

Heroku 教程遗漏了一些细节(或需要更新):

需要更新的配置:

aws.config.region = 'us-west-1';
aws.config.update({
  accessKeyId:  process.env.AWS_ACCESS_KEY_ID,
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
})