一尘不染

使用签名的URL将文件从angularjs直接上传到Amazon s3

angularjs

因此,我在将文件直接上传到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});
});

然后,我的角度控制器尝试使用该签名的URL($ scope.uploadDocument())直接上传到s3。

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

我知道S3CORS在该存储桶的亚马逊端已正确设置,因为我已经开发了将相同存储桶用于开发存储的ruby应用程序。(请允许我使用回形针和雾器)。其次,由于我没有亚马逊响应的失败原因,所以我不怀疑错误是从那里来的。但是它确实来自我尝试将文件放在亚马逊上的那一行。

因此,我确定我会丢失某些内容,但是我认为使用签名URL,除了对该URL进行放置外,不需要任何其他操作。


阅读 299

收藏
2020-07-04

共1个答案

一尘不染

我一直在这个问题上苦苦挣扎,终于弄明白了!我将详细说明我的步骤,希望它可以帮助一些人。

我使用了这个模块:https :
//github.com/asafdav/ng-s3upload

我按照他们列出的步骤进行操作,即:

  1. 创建一个桶
  2. 授予“放入/删除”:展开“权限”部分,然后单击“添加更多权限”按钮,选择“所有人”和“上传/删除”并保存。
  3. 添加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>
  1. 将“ 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>
  1. 创建一个将返回以下内容的JSON的服务:
        {
       "policy":"XXX",
       "signature":"YYY",
       "key":"ZZZ"
    }
  • XXX-AWS要求的策略json,采用base64编码。
  • YYY-HMAC和您的私钥
  • ZZZ-您的公钥这是Rails的示例,即使您不是Rails开发人员,也请阅读代码,这非常简单。

这是最重要的步骤:确保您生成正确的策略文档。

这是我在C#中的代码

            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出现问题。但是使用上述方法,我能够使用chrome从本地开发机上载文件。

Access-Control-Allow-Origin不允许来源’localhost:3000’

从您的错误看来,您尚未在AWS端设置CORS规则。

2020-07-04