如何使用预先签名的URL而不是凭据直接从浏览器上传到AWS S3?

use*_*332 28 javascript amazon-s3 amazon-web-services

我们想使用Javascript AWS SDK将文件上传到S3,但根本不使用凭据.使用凭据上传有效,但我们无法为每个应用用户生成AWS IAM用户(或者我们应该这样做?)

因此,与使用GET类似,我们希望服务器生成预签名URL,将其发送到浏览器,并将浏览器上传到该URL.

但是,没有关于如何实现这一目标的例子.此外,如果没有设置凭证,即使在上传到S3请求之前,SDK也会出错

code: "CredentialsError"
message: "No credentials to load"
Run Code Online (Sandbox Code Playgroud)

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);
});
Run Code Online (Sandbox Code Playgroud)

Jör*_*eld 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']);
Run Code Online (Sandbox Code Playgroud)

请确保您的用户身份验证添加到您的api.php.知道该文件路径的每个人都可以将文件上传到您的存储桶.

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
            )
        )
    )
);
Run Code Online (Sandbox Code Playgroud)

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...');
            }
        })
    }
};
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)


Bog*_*luk 6

在项目中,就我现在正在执行的工作而言,我将文件直接从客户端上传到S3,在我的情况下,它可以通过几个步骤工作:

  1. 从服务器请求带有上载设置的预签名表单(它是在服务器上签名的,因为我无法将访问密钥传递给客户端,并且还需要应用一些限制才能上载)
  2. 使用XHR2将文件上传到S3(对于旧的浏览器,您可以对隐藏的iframe或Flash等浏览器插件使用hack)

它有主要的代码部分:https : //gist.github.com/zxbodya/3cdabd9172bcc89f8ac5

  • 欢迎来到SO。通常会显示答案中所需的代码中最相关的部分,以便将搜索保存在网络上。 (6认同)

Sco*_*rth 6

如果您不使用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 
Run Code Online (Sandbox Code Playgroud)

请参阅文件对象文档: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);
  }
};
Run Code Online (Sandbox Code Playgroud)

  • 在尝试了很多不同的方法之后,这是第一次。谢谢斯科特 (2认同)

fae*_*anj 0

我可以建议两种方法:

1-您可以在应用程序中生成一份预签名表单,其中包含一个凭据

请参阅文档:http ://docs.aws.amazon.com/AmazonS3/latest/dev/HTTPPOSTForms.html

2- 您可以使用网络身份联合并使用 google、facebook 或 amazon 登录:

请参阅文档: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