web-dev-qa-db-ja.com

認証情報の代わりに事前に署名されたURLを使用して、ブラウザーからAWS S3に直接アップロードする方法

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);
});
26
user2528332

古い質問を静かにしてください、しかしそれは最終的にそれを成し遂げるために私を少し助けました。私のソリューションは、PHPとjQueryを使用したJavaScriptに基づいています。

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']);

必ず、ユーザー認証を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
            )
        )
    )
);

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>
17
Jörn Berkefeld

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 

Fileオブジェクトのドキュメントを参照してください: 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);
  }
};
6
Scott Jungwirth

プロジェクトでは、現在作業しているものについて、クライアントから直接S3にファイルをアップロードしています。私の場合は、いくつかの手順で機能します。

  1. アップロードの設定を含む事前署名済みフォームをサーバーから要求します(アクセスキーをクライアントに渡すことができないため、サーバーで署名されます。また、アップロードにいくつかの制限を適用する必要があります)
  2. xHR2を使用してファイルをS3にアップロードします(古いブラウザーの場合、非表示のiframeまたはフラッシュのようなブラウザープラグインでハックを使用できます)

それからの主要なコード部分があります: https://Gist.github.com/zxbodya/3cdabd9172bcc89f8ac5

5
Bogdan Savluk

ACLContentTypeを追加してください、それが動作するようになります。

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

私は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.'); }
});
1
user2528332

2つのアプローチを提案できます。

1- 1つの資格情報を使用して、アプリで事前署名済みフォームを生成できます

ドキュメントを参照してください: http://docs.aws.Amazon.com/AmazonS3/latest/dev/HTTPPOSTForms.html

2- Web IDフェデレーションを使用し、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

1
faermanj