2015-12-18 31 views
6

Sto creando un'applicazione Web che include una funzione di caricamento file. Il mio obiettivo è avviare il caricamento dagli utenti direttamente su un bucket S3. La strategia è pre-firmare una richiesta POST che verrà inviata come modulo.Caricamento basato su browser Amazon AWS S3 tramite POST -

Il roadblock è un errore SignatureDoesNotMatch - per quanto posso dire che sono conforme alla documentazione e ho esplorato molte opzioni, ma non riesco ancora a risolverlo. Sono in grado di generare collegamenti di download pre-impostati.

Referencing:

AWS POST documentation

Example

boto3 generate_presigned_post reference

Genera richiesta firmata:

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

Upload modulo (compilato con fields sopra):

<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> 

parte rilevante della risposta:

<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 decodifica di StringToSign al precedente errore:

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

risposta

2

trovato una soluzione: ha dovuto configurare esplicitamente il cliente s3 a usa la nuova firma v4 di Amazon. L'errore si verifica dal momento che ha come valore predefinito una versione precedente, causando la mancata corrispondenza. Un po 'di facepalm - al momento questo non è stato scritto in documenti boto3, anche se la gente di Amazon dice che dovrebbe essere presto.

Il metodo è semplificata in quanto restituisce ora esattamente i campi richiesti:

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 
    ) 

Il che significa che la forma può essere facilmente generato:

<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> 

Acclamazioni

+0

grande soluzione! Ho provato a implementarlo a modo tuo, ma sto ancora ricevendo lo stesso errore da S3. Ho notato che i campi restituiti non includono un "Content-Type". Sei riuscito a farlo funzionare senza includere il tipo di contenuto? – Brosef

+0

@Brosef Non ricordare quali campi sono stati restituiti da generate_presigned_post. Credo che la biblioteca sia progettata per restituire tutti i campi necessari. Hai controllato gli ultimi documenti di boto? Potrebbero essere cambiati dalla mia risposta. –

Problemi correlati