Python 使用POST基于亚马逊AWS S3浏览器的上传-

Python 使用POST基于亚马逊AWS S3浏览器的上传-,python,post,amazon-web-services,amazon-s3,boto3,Python,Post,Amazon Web Services,Amazon S3,Boto3,我正在构建一个包含文件上传功能的web应用程序。我的目标是启动从用户直接上传到S3存储桶的过程。策略是预先签署一个POST请求,该请求将作为表单提交 这个障碍是一个signaturedesnotmatch错误-据我所知,我已经遵守了文档,并探索了许多选项,但仍然无法解决。我能够生成预先签名的下载链接 参考: 生成已签名的请求: def s3_upload_creds(name, user): s3 = boto3.client('s3') key = '${filename

我正在构建一个包含文件上传功能的web应用程序。我的目标是启动从用户直接上传到S3存储桶的过程。策略是预先签署一个POST请求,该请求将作为表单提交

这个障碍是一个
signaturedesnotmatch
错误-据我所知,我已经遵守了文档,并探索了许多选项,但仍然无法解决。我能够生成预先签名的下载链接

参考:

生成已签名的请求:

def s3_upload_creds(name, user):
    s3 = boto3.client('s3')
    key = '${filename}'
    region = 'us-east-1'
    date_short = datetime.datetime.utcnow().strftime('%Y%m%d')
    date_long = datetime.datetime.utcnow().strftime('%Y%m%dT000000Z')
    fields = { 
        'acl': 'private',
        'date': date_short,
        'region': region,
        'x-amz-algorithm': 'AWS4-HMAC-SHA256',
        'x-amz-date': date_long
    }

    return s3.generate_presigned_post(
        Bucket = 'leasy',
        Fields = fields,
        Key = key,
        Conditions = [
            {'acl': 'private'},
            {'x-amz-algorithm': 'AWS4-HMAC-SHA256'},
            {'x-amz-credential': '/'.join(['AKI--snip--', date_short, region, 's3', 'aws4_request'])},
            {'x-amz-date': date_long}
        ]
    )
上传表单(用上面的
字段填充):

上述错误中
StringToSign
的Base64解码:

{u'conditions': [{u'acl': u'private'},
                 {u'x-amz-algorithm': u'AWS4-HMAC-SHA256'},
                 {u'x-amz-credential': u'AKI--snip--/20151218/us-east-1/s3/aws4_request'},
                 {u'x-amz-date': u'20151218T000000Z'},
                 {u'bucket': u'leasy'},
                 [u'starts-with', u'$key', u'']],
 u'expiration': u'2015-12-18T04:59:32Z'}

找到了一个解决方案:必须显式配置s3客户端以使用Amazon的新签名v4。由于默认为旧版本,因此会发生此错误,从而导致不匹配。有点像掌纹——虽然亚马逊的人说这应该很快就会出现,但当时这并不是用boto3文档写的

该方法简化了,因为它现在完全返回所需的字段:

def s3_upload_creds(name):
    BUCKET = 'mybucket'
    REGION = 'us-west-1'
    s3 = boto3.client('s3', region_name=REGION, config=Config(signature_version='s3v4'))
    key = '${filename}'
    return s3.generate_presigned_post(
        Bucket = BUCKET,
        Key = key
    )
这意味着可以轻松生成表单:

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  </head>
  <body>
    {{ creds }}
      <form action="https://mybucket.s3.amazonaws.com" method="post" enctype="multipart/form-data">
        {% for key, value in creds.fields.items() %}
          <input type="hidden" name="{{ key }}" value="{{ value }}" />
        {% endfor %}
      File:
      <input type="file"   name="file" /> <br />
    <input type="submit" name="submit" value="Upload to Amazon S3" />
  </form>
</html>

{{creds}}
{%为键,creds.fields.items()中的值%}
{%endfor%}
文件:


欢呼声

距离上次回复已经有几年了,但我在这方面已经坚持了一两天,因此我将向任何可能有帮助的人分享我的经验

当我试图通过预先签名的url上传到s3存储桶时,我遇到了一个错误:“403:您提供的AWS访问密钥Id在我们的记录中不存在”

我能够使用服务器端代码成功地生成一个与上面类似的预签名url:

signed_url_dict = self.s3_client.generate_presigned_post(
                self.bucket_name,
                object_name,
                ExpiresIn=300
这返回了一个具有以下结构的字典:

{
    url: "https://___",
    fields: {
        key: "___",
        AWSAccesKeyId: "___",
        x-amz-security-token: "___",
        policy: "___",
        signature: "___"
    }
}
这导致2019年浏览器端javascript的情况有所不同,所需的表单输入似乎已经改变。我没有像OP那样设置表单,而是创建了如下所示的表单:

<form action="https://pipeline-poc-ed.s3.amazonaws.com/" method="post" enctype="multipart/form-data" name="upload_form">
            <!-- Copy ALL of the 'fields' key:values returned by S3Client.generate_presigned_post() -->
            <input type="hidden" name="key" value="___" />
            <input type="hidden" name="AWSAccessKeyId" value="___" />
            <input type="hidden" name="policy" value="___"/>
            <input type="hidden" name="signature" value="___" />
            <input type="hidden" name="x-amz-security-token" value="___" />
        File:
            <input type="file"   name="file" /> <br />
            <input type="submit" name="submit" value="Upload to Amazon S3" />
        </form>

也许函数的结果是区域特定的?

在我的例子中,我正在生成一个Base64编码的表单

这个问题是由于Firefox固有地将策略和安全令牌值编码为Base64编码


因此存在双重编码,因此签名不符合要求。

很好的解决方案!尝试用你的方式实现它,但我仍然从S3中得到同样的错误。我注意到返回的字段不包含“内容类型”。你能在不包含内容类型的情况下让它工作吗?@Brosef不记得generate_presigned_post返回了哪些字段。我相信这个库是为了返回所有必要的字段而设计的。你查过最新的boto文件了吗?他们可能已经改变了,因为我的答案。非常感谢你张贴工作样本代码!
{
    url: "https://___",
    fields: {
        key: "___",
        AWSAccesKeyId: "___",
        x-amz-security-token: "___",
        policy: "___",
        signature: "___"
    }
}
<form action="https://pipeline-poc-ed.s3.amazonaws.com/" method="post" enctype="multipart/form-data" name="upload_form">
            <!-- Copy ALL of the 'fields' key:values returned by S3Client.generate_presigned_post() -->
            <input type="hidden" name="key" value="___" />
            <input type="hidden" name="AWSAccessKeyId" value="___" />
            <input type="hidden" name="policy" value="___"/>
            <input type="hidden" name="signature" value="___" />
            <input type="hidden" name="x-amz-security-token" value="___" />
        File:
            <input type="file"   name="file" /> <br />
            <input type="submit" name="submit" value="Upload to Amazon S3" />
        </form>
{
  "url": "https://__",
  "fields": {
    "key": "___",
    "x-amz-algorithm": "___",
    "x-amz-credential": "___",
    "x-amz-date": "___",
    "x-amz-security-token": "___",
    "policy": "___",
    "x-amz-signature": "___"
  }
}