Подтвердить что ты не робот

Как загрузить в AWS S3 прямо из браузера с использованием предварительно подписанного URL вместо учетных данных?

Мы хотели бы использовать 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);
});
4b9b3361

Ответ 1

Тихий старый вопрос, но это помогло мне немного сделать это наконец. Мое решение основано на 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']);

убедитесь, что добавьте аутентификацию пользователя на свой 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>

Ответ 2

В проекте, на том, что я сейчас работаю, я загружаю файлы непосредственно с клиента на S3, в моем случае он работает в несколько шагов:

  • запросить предварительно подписанную форму с настройками для загрузки, с сервера (она подписана на сервере, потому что я не могу передать ключи доступа клиенту, а также мне нужно применить некоторые ограничения для загрузки)
  • загрузить файл на S3 с помощью XHR2 (для старых браузеров вы можете использовать взломать скрытые iframe или плагины браузера, такие как flash).

Из него есть основные части кода: https://gist.github.com/zxbodya/3cdabd9172bcc89f8ac5

Ответ 3

Если вы не используете 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);
  }
};

Ответ 4

Я мог бы предложить два подхода:

1- Вы можете создать предварительно подписанную форму в своем приложении с одним удостоверением

См. doc: 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

Ответ 5

Я предпочитаю этот подход с помощью github:

Если у вас уже есть назначенный URL-адрес, созданный для браузера, вы можете просто отправить запрос XHR с этим URL-адресом и загрузкой для загрузки на 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

Добавьте 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);
    });