我们希望使用Javascript AWS SDK将文件上传到S3,但根本不使用凭据。 使用凭证上传有效,但我们无法为每个应用用户生成AWS IAM用户(或者我们应该这样做?)
因此,与使用GET类似,我们希望服务器生成预签名的URL,将其发送到浏览器,并将浏览器上传到该URL。
但是,没有关于如何实现这一目标的例子。 此外,如果没有设置凭证,即使在上传到S3请求之前,SDK也会错误
code: "CredentialsError"
message: "No credentials to load"
JS SDK文档提到了这一点,所以似乎有可能:
Pre-signing a putObject (asynchronously)
var params = {Bucket: 'bucket', Key: 'key'};
s3.getSignedUrl('putObject', params, function (err, url) {
console.log('The URL is', url);
});
答案 0 :(得分:18)
安静的问题,但它确实帮助我最终完成它。 我的解决方案基于PHP和JavaScript和jQuery。
我将整个解决方案完美地包裹在https://github.com/JoernBerkefeld/s3SignedUpload,但以下是必需品:
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']);
请务必
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...');
}
})
}
};
s3 cors设置(实际上需要PUT和OPTIONS,但不能直接启用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>
答案 1 :(得分:5)
如果您没有使用jQuery,那么这是您在前端所需的最小值:
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
请参阅文件对象文档:https://developer.mozilla.org/en-US/docs/Web/API/File
然后您可以照常添加上传进度:
xhr.upload.onprogress = (event) => {
if (event.lengthComputable) {
var percent = Math.round((event.loaded / event.total) * 100)
console.log(percent);
}
};
答案 2 :(得分:4)
在项目中,关于我现在正在工作的内容,我将文件从客户端直接上传到S3,在我的情况下,它只需几步即可完成:
主要代码部分来自:https://gist.github.com/zxbodya/3cdabd9172bcc89f8ac5
答案 3 :(得分:2)
生成网址
const AWS = require("aws-sdk");
const s3 = new AWS.S3({
endpoint: 's3-ap-south-1.amazonaws.com', // Put you region
accessKeyId: 'AKXXXXXXXXXXXXXXXA6U', // Put you accessKeyId
secretAccessKey: 'kzFHoXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXssoGp', // Put you accessKeyId
Bucket: 'Bucket-Name', // Put your bucket name
signatureVersion: 'v4',
region: 'ap-south-1' // Put you region
});
const getSingedUrlforPut = async () => {
const params = {
Bucket: 'Bucket-Name',
Key: '317ec11af14a46b89f400bcf8f9fff1222.pdf',
Expires: 60 * 5
};
try {
const url = await new Promise((resolve, reject) => {
s3.getSignedUrl('putObject', params, (err, url) => {
err ? reject(err) : resolve(url);
});
});
console.log(url)
} catch (err) {
if (err) {
console.log(err)
}
}
}
getSingedUrlforPut()
通过ajax上传文件
var form = new FormData();
form.append("", fileInput.files[0], "director_pan_af8ef2d261c46877f95038622c96e7c0.pdf");
var settings = {
"url": "https://sme-testing.s3-ap-south-1.amazonaws.com/317ec11af14a46b89f400bcf8f9fff1222.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIXXXXXXXXXXXX6U%2F20200525%2Fap-south-1%2Fs3%2Faws4_request&X-Amz-Date=20200525T083419Z&X-Amz-Expires=300&X-Amz-Signature=ea063731d7d043b62d0dc7c0984f4d5792c7f7f41e9ffb52a97d62adadcef422&X-Amz-SignedHeaders=host",
"method": "PUT",
"timeout": 0,
"processData": false,
"mimeType": "multipart/form-data",
"contentType": false,
"data": form
};
$.ajax(settings).done(function (response) {
console.log(response);
});
答案 4 :(得分:1)
我可以建议两种方法:
1-您可以在应用中使用一个凭据生成预签名表单
请参阅doc:http://docs.aws.amazon.com/AmazonS3/latest/dev/HTTPPOSTForms.html
2-您可以使用网络身份联合并使用google,facebook或amazon登录:
请参阅doc:http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/browser-configuring-wif.html
游乐场:http://aws.typepad.com/aws/2013/08/the-aws-web-identity-federation-playground.html
答案 5 :(得分:1)
我更喜欢这种更清洁的方法,通过github:
如果您已经为浏览器生成了预先签名的URL,则只需发送带有该URL和有效负载的XHR请求即可上传到S3。 SDK不需要这样做。下面是一个jQuery示例:
$.ajax({
url: presignedUrl, // the presigned URL
type: 'PUT',
data: 'data to upload into URL',
success: function() { console.log('Uploaded data successfully.'); }
});
答案 6 :(得分:1)
请添加ACL
和ContentType
,它会让它发挥作用。
const param = {
Bucket: 'Bucket',
Key: 'fiileName',
ACL: 'public-read',
ContentType: 'fileType'
};
s3.getSignedUrl('putObject', param, function (err, url) {
console.log('The URL is', url);
});