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);
});
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>
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);
}
};
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:
Il contient les principales parties du code: https://Gist.github.com/zxbodya/3cdabd9172bcc89f8ac5
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);
});
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.'); }
});
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