getSignedUrl不允许从浏览器上传

问题描述

我已经尝试了几天使s3上传直接从浏览器工作。

我正在使用jQuery ajax请求在服务器上查询签名的url(以确保用户已通过身份验证)。 然后,我返回签名的URL,并使用另一个jQuery ajax调用来尝试上传文件。

我的后端node.js代码:

exports.getSignedUrl = async (key,content) => {
  
  try {

    if (!key){
      return Promise.reject('No key supplied');
    }
    if (!content){
      return Promise.reject('No content type supplied');
    }
    
    const s3 = new AWS.S3({
      region: 'eu-west-1',signatureVersion: 'v4'
    });
    let params = {
      Bucket: siteSettings.s3Bucket,Key: key,ContentType: content,Expires: process.env.S3_UPLOAD_TIME || 240
    };
  
    let url = await s3.getSignedUrl('putObject',params);
  
    return Promise.resolve(url);

  } catch (error) {
    return Promise.reject(error);
  }

}

传递的params变量包含:

{
    Bucket:"[bucketname]"
    ContentType:"image/jpeg"
    Key:"ctl/5f43d07cbeb97850f4fa5246-small.jpg"
}

这会产生如下网址:

https://[bucketname].s3.eu-west-1.amazonaws.com/ctl/5f43d07cbeb97850f4fa5246-small.jpg?Content-Type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAXBXXXXXXXXXXBWVG%2F20200923%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20200923T101959Z&X-Amz-Expires=900&X-Amz-Signature=474415e11b9eec7636ab8a1995a28f92cebab18949fb981d37b6c6772209f039&X-Amz-SignedHeaders=host

我的浏览器代码:

const getActiveS3SignedUri = (filename,content,callback) => {

  $.ajax({
    method: 'POST',url: baseUrl + '/aws/getsignedurl',data: {
      filename: filename,content: content
    },dataType: 'json',complete: (data) => {
      if (data.responseJSON && data.responseJSON.success==true){
        callback(null,data.responseJSON);
      } else {
        if (data.responseJSON.err || data.responseJSON.error){
          callback(data.responseJSON.err || data.responseJSON.error);
        } else {
          callback(`Couldn't get URL,unknown error`);
        }
      }
    }
  })
}

const uploadToS3 = (url,fileObject,callback) => {

  let file = document.getElementById(fileObject).files[0];

  $.ajax({
    method: 'POST',// I have tried both POST and PUT here
    headers: {"Content-Type": file.type},processData: false,url: url,data: file,complete: (data) => {
      callback(null,data);
    }
  })
}

$(() => {

  $('#summernote').summernote({
    callbacks: {
      onImageUpload: function(files) {
        // upload image to S3 server and create imgNode...
        
        $summernote.summernote('insertNode',imgNode);
      }
    }
  });

  $('#validate-target').on('submit',(e) => {

      //- let input = $('#input_dummy');
      //- let val = $('#input_quill > .ql-editor');
      //- input.val(val.html());

  });

  $('#summernote-input').on('change',(e) => {
    let fileInput = $(e.target);
    let file = fileInput[0].files[0];

    getActiveS3SignedUri(file.name,file.type,(err,response) => {
      if (!err){
        uploadToS3(response.url,'summernote-input',data) => {
          alert(JSON.stringify(data));
        });
      }
    })
  });

这会将以下内容发布到AWS URL:

Host: [bucketname].s3.eu-west-1.amazonaws.com
Connection: keep-alive
Content-Length: 12147
Accept: */*
DNT: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/85.0.4183.102 Safari/537.36
Content-Type: image/jpeg
Origin: http://localhost:3000
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:3000/admin/settings/
Accept-Encoding: gzip,deflate,br
Accept-Language: en-US,en;q=0.9,en-GB;q=0.8

带有请求有效载荷:

Payload screenshot

我经常遇到以下错误:

<?xml version="1.0" encoding="UTF-8"?>
<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>AKIAXBXXXXXXXXXXBWVG</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
20200923T102902Z
20200923/eu-west-1/s3/aws4_request
9d9d71f4434cbbc236336d33db9e377e2c5790e1acb200e35ece8d25bc96c15b</StringToSign><SignatureProvided>41f375741726f222bcb86018ffae317f9e2a58891105b46783d5f7b343c97352</SignatureProvided><StringToSignBytes>...truncated...</StringToSignBytes><CanonicalRequest>POST
/ctl/5f43d07cbeb97850f4fa5246-small.jpg
Content-Type=image%2Fjpeg&amp;X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIAXBWXRFJB4RKEBWVG%2F20200923%2Feu-west-1%2Fs3%2Faws4_request&amp;X-Amz-Date=20200923T102902Z&amp;X-Amz-Expires=900&amp;X-Amz-SignedHeaders=host
host:toy-lib-dev-bucket.s3.eu-west-1.amazonaws.com

host
UNSIGNED-PAYLOAD</CanonicalRequest><CanonicalRequestBytes>...truncated...</CanonicalRequestBytes><RequestId>470D4BB6D67CDF82</RequestId><HostId>GmktGQDhTmi5a/26bR82tY/SE0C1DQdSZnGNAyp6VGmpYBoTgKXFuTlAYuZpYVCBgdc1Grck6CE=</HostId></Error>

我的s3存储桶CORS配置是:

<?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>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

我注意到返回的URL中的X-Amz-Date比现在晚了一个小时,我假设我现在处于BST时区的UTC,这可能引起问题吗?我尝试将有效期设置为3700秒,但这没有帮助。

到目前为止,我已经尝试了以下方法以使其正常工作:

  • 在POST和PUT方法之间更改
  • 已删除的过期超时
  • 已删除的内容类型
  • 指定的内容类型为表单/多部分
  • 尝试未在S3对象上指定区域和/或signatureVersion
  • 已将仅文件发送的数据更改为对象{file: file}

到目前为止,我没有任何进展,我错过了什么吗?

编辑:

我尝试使用fetch代替$.ajax,但这似乎没有问题:

fetch(url,{
  method: "PUT",body: file,});

解决方法

@bendataclear将图像文件转换为blob或base64字符串,然后对其进行处理。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...