使用签名的 url 将文件从 angularjs 直接上传到亚马逊 s3 [英] upload file from angularjs directly to amazon s3 using signed url

查看:34
本文介绍了使用签名的 url 将文件从 angularjs 直接上传到亚马逊 s3的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我在将文件直接上传到 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',功能($scope,$http,customProvider,custom){$scope.fileTypes =["类型 1",类型 2"]$scope.setFile = 函数(元素){$scope.$apply(function($scope){$scope.currentDocument = element.files[0];});}$scope.uploadDocument = function() {$http.post('/s3SignedURL', {fileName: $scope.currentDocument.name} ).成功(功能(结果){$http.put(results.signedUrl, $scope.currentDocument).成功(功能(){custom.document = s3FileName;customProvider.save(自定义,函数(){//..在这里做点什么});});});};}]);

我的 html 表单看起来像

<label for="documentType">文件类型</label><select class="form-control" ng-model="docType" ng-options="type for type in fileTypes" required ><option value=""/></选择><label for="filename">选择要上传的文件</label><输入类型=文件"名称=s3File"onchange="angular.element(this).scope().setFile(this)"ng-model="文件名"需要/><input type="submit" value="上传文件"></表单>

然而,每当我尝试上传到 S3 时,我都会收到错误

Access-Control-Allow-Origin 不允许访问 http://localhost:3000

我知道 S3 CORS 已在亚马逊端为该存储桶正确设置,因为我开发了使用相同存储桶进行开发存储的 ruby​​ 应用程序.(当然,我使用的是回形针和雾气).其次,由于我没有针对亚马逊响应的失败捕获,我不怀疑错误来自那里.但是,它确实来自我尝试将文件放在亚马逊上的那一行.

所以我确定我遗漏了一些东西,但我认为对于签名的 URL,我不需要做任何事情,只需要对那个 url 做一个 put.

解决方案

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

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

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

  1. 创建存储桶
  2. 授予放置/删除"权限:展开权限"部分并点击添加更多权限"按钮.选择所有人"和上传/删除"并保存.
  3. 添加 CORS 配置:

  4. 将crossdomain.xml"添加到您的存储桶的根目录以使其公开

    <!DOCTYPE 跨域策略系统http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd"><跨域策略><allow-access-from domain="*" secure="false"/></跨域策略>

  5. 创建一个返回 JSON 的服务,内容如下:

    <代码>{"政策":"XXX","签名":"YYY","key":"ZZZ"}

<块引用>

  • XXX - AWS 所需的策略 json,base64 编码.
  • YYYY -您的私钥的 HMAC 和 sha
  • ZZZ - 你的公钥rails 示例,即使您不是 Rails 开发人员,也请阅读代码,非常简单.

这是最重要的一步:确保生成正确的政策文件.

这是我的 C# 代码

 StringBuilder builder = new StringBuilder();builder.Append("{").Append("\"过期时间\": \"").Append(GetFormattedTimestamp(expireInMinutes)).附加("\",").Append("\"条件\": [").Append("{\"bucket\": \"").Append(bucketName).附加("\"},").Append("{\"acl\": \"").Append("公开阅读").附加("\"},").Append("[\"starts-with\", \"$key\", \"").Append(前缀).附加("\"],").Append("[\"starts-with\", \"$Content-Type\", \"\"],").Append("[ \"content-length-range\", 0, " + 10 * 1024 * 1024 + "]").附加("]}");编码编码 = new UTF8Encoding();this.policyString = Convert.ToBase64String(encoding.GetBytes(builder.ToString().ToCharArray()));this.policySignature = SignPolicy(awsSecretKey, policyString);

这将生成以下 Json

<代码>{"到期":"2014-02-13T15:17:40.998Z",状况":[{"bucket":"bucketusaa"},{"acl":"公开阅读"},[以..开始","$key","],[以..开始","$内容类型","],[内容长度范围",0,10485760]]}

这个文档然后被 base64 编码并作为字符串发送.

我的问题是我的保单文件.策略文档就像您为会话定义的一组规则,例如:文件名必须以某些内容开头(即上传到子文件夹),大小必须在范围内.

为您的浏览器使用开发人员工具,并查看网络选项卡,看看 AWS 返回了哪些错误,这对我很有帮助,它会说明诸如策略错误之类的内容并说明失败的条件.您通常会收到拒绝访问错误,这取决于策略文档中设置的条件或错误的密钥.

另外一件事是一些浏览器在本地主机 CORS 方面存在问题.但是使用上述内容,我能够使用 chrome 从我的本地开发机器上传文件.

<块引用>

Access-Control-Allow-Origin 不允许 Origin 'localhost:3000'

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

So I am having some trouble uploading a file directly to S3. Currently my process is to make a request to nodejs/express to get a signed 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});
});

My angular controller then tries to upload directly to s3 using that signed 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
        });
      });
    });
  };
}]);

My html form looks like

<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>

However whenever I try to upload to S3 I get the error

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

I know that S3 CORS is setup correctly, on the amazon end, for that bucket, because I have developed ruby apps that use the same bucket for development storage. (granted I was using paperclip & fog for those). Secondly, since I don't have a failure catch for the amazon response, I don't suspect the error to be coming from there. However it does come from the line where I try to put the file on amazon.

So I am sure I am missing something, but I thought that with signed URL's I don't need anything more than to do a put to that url.

解决方案

I have been struggling a lot with this issue and finally got it figured out! I will detail my steps, hopefully it can help some one out.

I used this module: https://github.com/asafdav/ng-s3upload

I followed the steps they listed, namely:

  1. Create a Bucket
  2. Grant "put/Delete: expand the "Permissions" sections and click on the "Add more permissions" button. Select "Everyone" and "Upload/Delete" and save.
  3. Add CORS Configuration:

    <?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>
    

  4. Add "crossdomain.xml" to the root of your bucket making it public

    <?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>
    

  5. Create a service that will return JSON with the following:

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

  • XXX - A policy json that is required by AWS, base64 encoded.
  • YYY - HMAC and sha of your private key
  • ZZZ - Your public key Here's a rails example, even if you're not a rails developer, read the code, it's very straight forward.

This is the most important step: make sure you are generating the correct policy document.

Here is my code in 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);

This generates the following 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
      ]
   ]
}

This document is then base64 encoded and sent down as a string.

My issue was with my policy document. The policy document is like a set of rules you define for the session like: file names must start with something (ie. upload to a subfolder), the size must be in the range.

Use the developer tools for your browser, and take a look at the network tab, see what errors AWS are returning this really helped me, it will state things like policy errors and say what condition failed. You will generally get access denied errors and this will be based on the conditions set in the policy document or wrong keys.

One other thing some browsers have issues with localhost CORS. But using the above I was able to upload files from my local dev machine using chrome.

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

From your error it looks like you have not set up the CORS rules on the AWS side.

这篇关于使用签名的 url 将文件从 angularjs 直接上传到亚马逊 s3的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆