Загружать файл из angularjs прямо в amazon s3 с помощью подписанного URL-адреса

Так что у меня возникли проблемы с загрузкой файла непосредственно на S3. В настоящее время мой процесс заключается в том, чтобы сделать запрос nodejs/express, чтобы получить подписанный URL.

app.post('/s3SignedURL', function(req, res){
  var id = crypto.randomBytes(20).toString('hex');
  var ext = path.extname(req.body.fileName);
  var unambFilename = path.basename(req.body.fileName, ext) + '-' + id + ext;
  var params = {Bucket: awsBucket, Key: unambFilename, Expires: 30};
  var signedUrl = s3.getSignedUrl('putObject', params);

  res.send({signedUrl: signedUrl, s3FileName: unambFilename});
});

Мой контроллер angular затем пытается загрузить непосредственно на s3 с помощью этого подписанного URL ($ scope.uploadDocument())

flqApp.controller('DocUploadModalCtrl', ['$scope', '$http', 'customProvider', 'custom',
  function($scope, $http, customProvider, custom){

  $scope.fileTypes = 
  [
    "Type 1",
    "Type 2"
  ]

  $scope.setFile = function(element){
    $scope.$apply(function($scope){
      $scope.currentDocument = element.files[0];
    });
  }

  $scope.uploadDocument = function() {
    $http.post('/s3SignedURL', {fileName: $scope.currentDocument.name} )
     .success(function(results){
      $http.put(results.signedUrl, $scope.currentDocument)
       .success(function(){
        custom.document = s3FileName;
        customProvider.save(custom, function(){
        //..do something here
        });
      });
    });
  };
}]);

Моя форма html выглядит как

<form ng-submit="uploadDocument()">
  <label for="documentType">File Type</label>
  <select class="form-control" ng-model="docType" ng-options="type for type in fileTypes" required >
    <option value=""/>
  </select>
  <label for="filename">Choose file to upload</label>
  <input type="file"
     name="s3File"
     onchange="angular.element(this).scope().setFile(this)"
     ng-model="fileName"
     required />

  <input type="submit" value="Upload File">
</form>

Однако всякий раз, когда я пытаюсь загрузить на S3, я получаю сообщение об ошибке

Origin http://localhost:3000 is not allowed by Access-Control-Allow-Origin

Я знаю, что S3 CORS правильно настроен на конце амазонки для этого ведра, потому что я разработал рубиновые приложения, которые используют тот же самый ведро для хранилища разработки. (при условии, что я использовал для этого скрепку и туман). Во-вторых, поскольку у меня нет сбоя сбоя для ответа амазонки, я не подозреваю, что ошибка придет оттуда. Однако это происходит из строки, где я пытаюсь поместить файл на amazon.

Итак, я уверен, что чего-то не хватает, но я думал, что с подписанным URL-адресом мне не нужно ничего больше, чем сделать этот URL-адрес.

Ответ 1

Я много борюсь с этой проблемой и, наконец, понял! Я подробно расскажу о своих шагах, надеюсь, это поможет кому-то.

Я использовал этот модуль: https://github.com/asafdav/ng-s3upload

Я выполнил следующие шаги:

  • Создать ведро
  • Грант "put/Delete: разверните разделы" Разрешения "и нажмите кнопку" Добавить дополнительные разрешения ". Выберите" Все "и" Загрузить/Удалить "и сохраните.
  • Добавить конфигурацию CORS:

    <?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>
            <AllowedHeader>*</AllowedHeader>
        </CORSRule>
    

  • Добавьте "crossdomain.xml" в корень вашего ведра, сделав его общедоступным.

    <?xml version="1.0"?>
    <!DOCTYPE cross-domain-policy SYSTEM
    "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
    <cross-domain-policy>
      <allow-access-from domain="*" secure="false" />
    </cross-domain-policy>
    
  • Создайте службу, которая вернет JSON со следующим:

    {
       "policy":"XXX",
       "signature":"YYY",
       "key":"ZZZ"
    }
    

  • XXX - политика json, требуемая AWS, закодированная base64.
  • YYY - HMAC и ваш секретный ключ.
  • ZZZ - Ваш открытый ключ Здесь например, даже если вы не разработчик рельсов, прочитайте код, это очень прямолинейно.

Это самый важный шаг: убедитесь, что вы создаете правильный документ политики.

Вот мой код в С#

            StringBuilder builder = new StringBuilder();
        builder.Append("{")
                .Append("\"expiration\": \"")
                .Append(GetFormattedTimestamp(expireInMinutes))
                .Append("\",")
                .Append("\"conditions\": [")
                .Append("{\"bucket\": \"")
                .Append(bucketName)
                .Append("\"},")
                .Append("{\"acl\": \"")
                .Append("public-read")
                .Append("\"},")
                .Append("[\"starts-with\", \"$key\", \"")
                .Append(prefix)
                .Append("\"],")
                .Append("[\"starts-with\", \"$Content-Type\", \"\"],")                    
                .Append("[ \"content-length-range\", 0, " + 10 * 1024 * 1024 + "]")
                .Append("]}");
        Encoding encoding = new UTF8Encoding();
        this.policyString = Convert.ToBase64String(encoding.GetBytes(builder.ToString().ToCharArray()));
        this.policySignature = SignPolicy(awsSecretKey, policyString);

Это генерирует следующий Json

{
   "expiration":"2014-02-13T15:17:40.998Z",
   "conditions":[
      {
         "bucket":"bucketusaa"
      },
      {
         "acl":"public-read"
      },
      [
         "starts-with",
         "$key",
         ""
      ],
      [
         "starts-with",
         "$Content-Type",
         ""
      ],
      [
         "content-length-range",
         0,
         10485760
      ]
   ]
}

Этот документ затем закодирован base64 и отправляется в виде строки.

Моя проблема была связана с моим политическим документом. Документ политики похож на набор правил, которые вы определяете для сеанса: имена файлов должны начинаться с чего-либо (т.е. Загружать в подпапку), размер должен быть в диапазоне.

Используйте инструменты разработчика для своего браузера и посмотрите на вкладку сети, посмотрите, какие ошибки AWS возвращают, это действительно помогло мне, оно будет указывать такие вещи, как ошибки политики, и сказать, какое условие не удалось. Обычно вы получите отказ в доступе к ошибкам, и это будет основано на условиях, установленных в документе политики или неправильных ключах.

Еще одна вещь, которую некоторые браузеры имеют проблемы с localhost CORS. Но, используя вышеизложенное, я смог загружать файлы с моей локальной машины-разработчика, используя хром.

Происхождение "localhost: 3000" не разрешено Access-Control-Allow-Origin

Из вашей ошибки кажется, что вы не настроили правила CORS на стороне AWS.

Ответ 2

Этот пример может помочь: https://github.com/bookingbricks/file-upload-example Использование: Node, aws-sdk-js, jQuery-file-upluad (blueimp)

Сервер:

var AWS = require('aws-sdk');

AWS.config.update({accessKeyId: AWS_ACCESS_KEY, secretAccessKey:     AWS_SECRET_KEY});
AWS.config.region = 'eu-west-1';

app.post('/s', function (req, res) {
    var s3 = new AWS.S3();
    var params = {Bucket: 'BUCKETNAME', Key: req.body.name, ContentType: req.body.type};
    s3.getSignedUrl('putObject', params, function(err, url) {
        if(err) console.log(err);
        res.json({url: url});
    });
});

Клиент:

$.ajax({
    url: '/s',
    type: 'POST',
    data: {name: file.name, size: file.size, type:file.type},
}).success(function(res){
    $.ajax({
        url: res.url,
        type: 'PUT',
        data: file,
        processData: false,
        contentType: file.type,
    }).success(function(res){
        console.log('Done');
    });