web-dev-qa-db-fra.com

Comment télécharger sur AWS S3 directement à partir du navigateur en utilisant une URL pré-signée au lieu des informations d'identification?

Nous aimerions utiliser Javascript AWS SDK pour télécharger des fichiers vers S3, mais sans utiliser d'informations d'identification. Le téléchargement à l'aide d'informations d'identification fonctionne, mais nous ne pouvons pas générer un utilisateur AWS IAM pour chacun de nos utilisateurs d'application (ou devrions-nous?)

Par conséquent, comme pour l'utilisation de GET, nous aimerions que le serveur génère une URL pré-signée, l'envoie au navigateur et que le navigateur télécharge vers cette URL.

Cependant, il n'y a aucun exemple sur la façon d'y parvenir. De plus, si vous ne définissez pas d'informations d'identification, même avant d'effectuer le téléchargement vers la demande S3, le SDK affiche des erreurs avec

code: "CredentialsError"
message: "No credentials to load"

Les documents du SDK JS le mentionnent, il semble donc possible:

Pre-signing a putObject (asynchronously)
var params = {Bucket: 'bucket', Key: 'key'};
    s3.getSignedUrl('putObject', params, function (err, url) {
      console.log('The URL is', url);
});
26
user2528332

Calmez la vieille question, mais cela m'a un peu aidé à le faire enfin. Ma solution est basée sur PHP et JavaScript avec jQuery.

J'ai la solution complète bien emballée à https://github.com/JoernBerkefeld/s3SignedUpload mais voici l'essentiel:

api.php:

<?php
require_once '/server/path/to/aws-autoloader.php';
use Aws\Common\Aws;

$BUCKET = "my-bucket";
$CONFIG = "path-to-iam-credentials-file-relative-to-root.php"

function getSignedUrl($filename, $mime) {
    $S3 = Aws::factory( $CONFIG )->get('S3');
    if(!$filename) {
        return $this->error('filename missing');
    }
    if(!$mime) {
        return $this->error('mime-type missing');
    }
    $final_filename = $this->get_file_name($filename);
    try {
        $signedUrl = $S3->getCommand('PutObject', array(
            'Bucket' => $BUCKET,
            'Key' => $this->folder . $final_filename,
            'ContentType' => $mime,
            'Body'        => '',
            'ContentMD5'  => false
        ))->createPresignedUrl('+30 minutes');
    } catch (S3Exception $e) {
        echo $e->getMessage() . "\n";
    }
    $signedUrl .= '&Content-Type='.urlencode($mime);
    return $signedUrl;
}


echo getSignedUrl($_GET['filename'],$_GET['mimetype']);

veuillez vous assurer d'ajouter l'authentification utilisateur à votre api.php. Sinon, tous ceux qui connaissent le chemin d'accès à ce fichier pourraient télécharger des fichiers dans votre compartiment.

credentials.inc.php:

<?php
return array(
    'includes' => array('_aws'),
    'services' => array(
        'default_settings' => array(
            'params' => array(
                'key'    => 'MY-ACCESS-KEY',
                'secret' => 'MY-SECRECT',
                'region'  => 'eu-west-1' // set to your region
            )
        )
    )
);

client.js:

$("input[type=file]").onchange = function () {
    for (var file, i = 0; i < this.files.length; i++) {
        file = this.files[i];
        $.ajax({
            url : s3presignedApiUri,
            data: 'file='+ file.name + '&mime=' + file.type,
            type : "GET",
            dataType : "json",
            cache : false,
        })
        .done(function(s3presignedUrl) {
            $.ajax({
                url : s3presignedUrl,
                type : "PUT",
                data : file,
                dataType : "text",
                cache : false,
                contentType : file.type,
                processData : false
            })
            .done(function(){
                console.info('YEAH', s3presignedUrl.split('?')[0].substr(6));
            }
            .fail(function(){
                console.error('damn...');
            }
        })
    }
};

paramètres des cors s3 (PUT & OPTIONS sont réellement nécessaires, mais ne peuvent pas activer directement OPTIONS ...):

<?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>
        <AllowedMethod>HEAD</AllowedMethod>
        <AllowedMethod>DELETE</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>
17
Jörn Berkefeld

Si vous n'utilisez pas jQuery, c'est le minimum dont vous avez besoin à l'avant:

var xhr = new XMLHttpRequest();
xhr.open('PUT', signedUrl, true);
xhr.setRequestHeader('Content-Type', signedUrlContentType);
xhr.onload = () => {
  if (xhr.status === 200) {
    // success!
  }
};
xhr.onerror = () => {
  // error...
};
xhr.send(file); // `file` is a File object here 

Voir les documents d'objet fichier: https://developer.mozilla.org/en-US/docs/Web/API/File

Ensuite, vous pouvez ajouter votre progression de téléchargement comme d'habitude:

xhr.upload.onprogress = (event) => {
  if (event.lengthComputable) {
    var percent = Math.round((event.loaded / event.total) * 100)
    console.log(percent);
  }
};
6
Scott Jungwirth

Dans le projet, sur ce que je travaille en ce moment, j'ai des téléchargements de fichiers du client directement vers S3, dans mon cas, cela fonctionne en quelques étapes:

  1. demander un formulaire pré-signé avec les paramètres de téléchargement, à partir du serveur (il est signé sur un serveur, car je ne peux pas transmettre les clés d'accès au client, et je dois également appliquer certaines limitations pour le téléchargement)
  2. télécharger un fichier sur S3 en utilisant XHR2 (pour les anciens navigateurs, vous pouvez utiliser le piratage avec iframe caché ou des plugins de navigateur comme flash)

Il contient les principales parties du code: https://Gist.github.com/zxbodya/3cdabd9172bcc89f8ac5

5
Bogdan Savluk

Veuillez ajouter ACL et ContentType, cela le fera fonctionner.

const param = {
      Bucket: 'Bucket',
      Key: 'fiileName',
      ACL: 'public-read',
      ContentType: 'fileType'
    };
s3.getSignedUrl('putObject', param, function (err, url) {
         console.log('The URL is', url);
    });
1
Ruta

Je préfère cette approche plus propre, via github:

Si vous avez déjà généré une URL prédéfinie pour le navigateur, vous pouvez simplement envoyer une demande XHR avec cette URL et la charge utile à télécharger sur S3. Le SDK ne serait pas tenu de le faire. Un exemple jQuery ci-dessous:

$.ajax({
  url: presignedUrl, // the presigned URL
  type: 'PUT',
  data: 'data to upload into URL',
  success: function() { console.log('Uploaded data successfully.'); }
});
1
user2528332

Je pourrais suggérer deux approches:

1- Vous pouvez générer un formulaire pré-signé dans votre application, avec un identifiant

Voir doc: http://docs.aws.Amazon.com/AmazonS3/latest/dev/HTTPPOSTForms.html

2- Vous pouvez utiliser la fédération d'identité Web et utiliser la connexion avec Google, Facebook ou Amazon:

Voir doc: http://docs.aws.Amazon.com/AWSJavaScriptSDK/guide/browser-configuring-wif.html

Aire de jeux: http://aws.typepad.com/aws/2013/08/the-aws-web-identity-federation-playground.html

1
faermanj