2015-12-18 2 views
6

Я создаю веб-приложение, которое включает функцию загрузки файлов. Моя цель - инициировать загрузку с пользователей непосредственно в ведро S3. Стратегия состоит в том, чтобы предварительно подписать запрос POST, который будет представлен как форма.Загрузка с Amazon AWS S3 с использованием POST -

Ошибка на дороге - SignatureDoesNotMatch Ошибка - насколько я могу судить, я согласен с документацией и изучил множество вариантов, но все еще неспособен разрешить. Я могу генерировать назначенные ссылки для загрузки.

Referencing:

AWS POST documentation

Example

boto3 generate_presigned_post reference

Сформировать подписанный запрос:

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} 
     ] 
    ) 

Загрузить форму (заполняется fields выше):

<html> 
    <head> 

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 

    </head> 
    <body> 
    {{ creds }} 
    <form action="{{ creds.url }}" method="post" enctype="multipart/form-data"> 
    Key to upload: 
    <input type="input" name="key" value="${filename}" /><br /> 
    <input type="input" name="acl" value="{{ creds.fields.acl }}" /> 
    <input type="hidden" name="Policy" value="{{ creds.fields.policy }}" /> 
    <input type="text" name="X-Amz-Algorithm" value="{{ creds.fields['x-amz-algorithm'] }}" /> 
    <input type="input" name="X-Amz-Credential" value="{{ creds.fields.AWSAccessKeyId }}/{{ creds.fields.date }}/us-east-1/s3/aws4_request" /> 
    <input type="input" name="X-Amz-Date" value="{{ creds.fields['x-amz-date'] }}" /> 
    <input type="input" name="X-Amz-Signature" value="{{ creds.fields.signature }}" /> 
    File: 
    <input type="file" name="file" /> <br /> 
    <!-- The elements after this will be ignored --> 
    <input type="submit" name="submit" value="Upload to Amazon S3" /> 
    </form> 

</html> 

Соответствующая часть ответа:

<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>AKI--snip--</AWSAccessKeyId> 
<StringToSign> 
eyJjb25kaXRpb25zIjogW3siYWNsIjogInByaXZhdGUifSwgeyJ4LWFtei1hbGdvcml0aG0iOiAiQVdTNC1ITUFDLVNIQTI1NiJ9LCB7IngtYW16LWNyZWRlbnRpYWwiOiAiQUtJQUlDVjRNVlBUUlFHU1lLV1EvMjAxNTEyMTgvdXMtZWFzdC0xL3MzL2F3czRfcmVxdWVzdCJ9LCB7IngtYW16LWRhdGUiOiAiMjAxNTEyMThUMDAwMDAwWiJ9LCB7ImJ1Y2tldCI6ICJsZWFzeSJ9LCBbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAiIl1dLCAiZXhwaXJhdGlvbiI6ICIyMDE1LTEyLTE4VDA1OjEwOjU2WiJ9 
</StringToSign> 
<SignatureProvided>wDOjsBRc0iIW7JNtz/4GHgfvKaU=</SignatureProvided> 

Base64 декодирование StringToSign в вышеуказанной ошибке:

{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'} 

ответ

2

Найдено решение: было явно настроить клиент s3 к используйте новую подпись Amazon v4. Ошибка возникает, поскольку по умолчанию используется старая версия, что вызывает несоответствие. Немного лица - в то время, когда это не было написано в документах boto3, хотя люди в Amazon говорят, что это должно быть скоро.

метод упрощается, так как он теперь возвращается точно требуются поля:

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> 

Приветствия

+0

Отличное решение! Попытался реализовать его по-своему, но я все равно получаю ту же ошибку от S3. Я заметил, что возвращенные поля не включают «Content-Type». Могли ли вы работать, не включая контент-тип? – Brosef

+0

@Brosef Не помните, какие поля были возвращены функцией generate_presigned_post. Я считаю, что библиотека предназначена для возврата всех необходимых полей. Вы проверили последние документы boto? Возможно, они изменились после моего ответа. –

 Смежные вопросы

  • Нет связанных вопросов^_^