2015-12-18 1 views
6

Je crée une application Web qui inclut une fonction de téléchargement de fichiers. Mon objectif est de lancer le téléchargement des utilisateurs directement vers un compartiment S3. La stratégie consiste à pré-signer une requête POST qui sera soumise sous forme de formulaire. Le roadblock est une erreur SignatureDoesNotMatch - autant que je sache, je me suis conformé à la documentation, et j'ai exploré beaucoup d'options, mais toujours incapable de résoudre. Je suis capable de générer des liens de téléchargement prédéfinis.Téléchargement via Amazon AWS S3 via POST -

Referencing:

AWS POST documentation

Example

boto3 generate_presigned_post reference

Générer une requête signée:

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

forme Upload (peuplé fields ci-dessus):

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

partie pertinente de la réponse:

<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 decode de StringToSign en erreur ci-dessus:

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

Répondre

2

trouvé une solution: a configurer explicitement le client s3 à utilisez la nouvelle signature d'Amazon v4. L'erreur se produit car elle est par défaut à une version plus ancienne, provoquant la discordance. Peu d'un visagepalm - à l'époque ce n'était pas écrit dans docs boto3, bien que les gens à Amazon disent que ce devrait être bientôt.

La méthode est simplifiée car elle retourne maintenant exactement les champs obligatoires:

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 
    ) 

Ce qui signifie que la forme peut être facilement généré:

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

Vive

+0

Grande solution! J'ai essayé de le mettre en pratique mais je reçois toujours la même erreur de S3. J'ai remarqué que les champs retournés n'incluent pas de "Content-Type". Avez-vous pu le faire fonctionner sans inclure le type de contenu? – Brosef

+0

@Brosef Ne pas se souvenir des champs retournés par generate_presigned_post. Je crois que la bibliothèque est conçue pour retourner tous les champs nécessaires. Avez-vous vérifié les derniers documents boto? Ils ont peut-être changé depuis ma réponse. –