【问题标题】:AngularJs Image upload to S3AngularJs 图像上传到 S3
【发布时间】:2016-01-03 20:23:16
【问题描述】:

我是:
- 创建 Web 应用程序
- 带有 ng 文件上传的 AngularJS 前端 (https://github.com/danialfarid/ng-file-upload)
- Node.js 后端
- 希望能够将图像上传到我的 Amazon S3 存储桶

我正在尝试学习本教程: https://github.com/danialfarid/ng-file-upload/wiki/Direct-S3-upload-and-Node-signing-example

基本上程序流程是选择文件,单击按钮,从后端请求签名,然后上传到 S3。

我收到来自后端的签名,代码为 200,但是当前端尝试上传图片时,我在开发者菜单中看到:
OPTIONS https://mybucket.name.s3-us-east-1.amazonaws.com/ net::ERR_NAME_NOT_RESOLVED

问题是我的代码还是我设置存储桶的方式?

如果需要,添加代码:

我的 S3 存储桶上的 CORS

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Authorization</AllowedHeader>
    </CORSRule>
    <CORSRule>
        <AllowedOrigin>my.computers.IP.Address</AllowedOrigin>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>DELETE</AllowedMethod>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

Node.js 后端代码

app.post('/signing', function(req, res) {
    var request = req.body;
    var fileName = request.filename

    var extension = fileName.substring(fileName.lastIndexOf('.'));
    var today = new Date();
    var path = '/' + today.getFullYear() + '/' + today.getMonth() + '/' + today.getDate() + '/' + uuid.v4() + extension;

    var readType = 'private';

    var expiration = moment().add(5, 'm').toDate(); //15 minutes

    var s3Policy = {
        'expiration': expiration,
        'conditions': [{
                'bucket': aws.bucket
            },
            ['starts-with', '$key', path], 
            {
                'acl': readType
            },
            {
              'success_action_status': '201'
            },
            ['starts-with', '$Content-Type', request.type],
            ['content-length-range', 2048, 10485760], //min and max
        ]
    };

    var stringPolicy = JSON.stringify(s3Policy);
    var base64Policy = new Buffer(stringPolicy, 'utf-8').toString('base64');

    // sign policy
    var signature = crypto.createHmac('sha1', aws.secret)
        .update(new Buffer(base64Policy, 'utf-8')).digest('base64');

    var credentials = {
        url: s3Url,
        fields: {
            key: path,
            AWSAccessKeyId: aws.key,
            acl: readType,
            policy: base64Policy,
            signature: signature,
            'Content-Type': request.type,
            success_action_status: 201
        }
    };
    res.jsonp(credentials);
});

AngularJS 前端代码

App.controller('MyCtrl2', ['$scope', '$http', 'Upload', '$timeout', function ($scope, $http, Upload, $timeout) {
    $scope.uploadPic = function(file) {
        var filename = file.name;
        var type = file.type;
        var query = {
            filename: filename,
            type: type
        };
        $http.post('/signing', query)
            .success(function(result) {
                Upload.upload({
                    url: result.url, //s3Url
                    transformRequest: function(data, headersGetter) {
                        var headers = headersGetter();
                        delete headers.Authorization;
                        return data;
                    },
                    fields: result.fields, //credentials
                    method: 'POST',
                    file: file
                }).progress(function(evt) {
                    console.log('progress: ' + parseInt(100.0 * evt.loaded / evt.total));
                }).success(function(data, status, headers, config) {
                    // file is uploaded successfully
                    console.log('file ' + config.file.name + 'is uploaded successfully. Response: ' + data);
                }).error(function() {

                });
            })
            .error(function(data, status, headers, config) {
                // called asynchronously if an error occurs
                // or server returns response with an error status.
        });
    };
}]);

【问题讨论】:

标签: angularjs node.js amazon-web-services amazon-s3 ng-file-upload


【解决方案1】:

该示例包含一些我认为过于简单化的错误。

var s3Url = 'https://' + aws.bucket + '.s3-' + aws.region + '.amazonaws.com';

这在很多时候都有效,但它不是一种始终在 S3 中制作指向对象的 URL 的有效方法。

它至少在两种情况下发生故障,其中一种是您遇到的情况。

S3 US Standard 区域的端点(位于 AWS 的 us-east-1 区域)不是 s3-us-east-1.amazonaws.com,因为它会如果它对所有其他地区使用相同的格式。由于似乎是遗留/进化的原因,它只是s3.amazonaws.com,但也可以写成s3-external-1.amazonaws.com。 (请记住,在撰写本文时,S3 已经存在了近十年,并且服务已经扩展和发展,同时保持向后兼容性——这是一个值得注意的壮举,但必然会导致一些乍一看令人困惑的约定。)

但是,所有存储桶——包括那些不在 us-east-1 中的存储桶——但不包括那些会因为第二个原因(我还没有谈到)而破坏上述代码的存储桶——可以简单地解决为bucket-name.s3.amazonaws.com -- 如果您考虑 DNS 的分层性质,您会看到它是如何工作的:S3 在创建存储桶后的几分钟内重新映射 DNS 中的特定主机名,以将请求发送到正确的区域 S3 端点。

所以+ '.s3-' + aws.region + 应该可以工作,只要写成+ '.s3' +

...当然,除非您创建了一个名称中带有点的存储桶。在这种情况下,您将遇到 https 问题(这是问题 #2,上面提到过),因为名称中带有点的存储桶与 S3 提供的通配符 SSL (TLS) 证书不匹配(SSL 中的设计限制通配符证书,而不是 S3 本身)。

如果这是一个问题,并且出于某种原因不希望使用名称中没有点的存储桶,则您的 URL 必须采用所谓的 path-style 格式。这是 virtual-style 格式的替代方案,其中存储桶名称位于主机名中。这里,存储桶名称是路径的第一部分:

https://s3[-region].amazonaws.com/bucket.name.with.dots/key-path

...同样,对于美国标准 (us-east-1),第一个组件只是 s3s3-external-1... 但是使用这种格式需要您匹配区域,与上述不同, DNS 处理请求路由的位置...否则 S3 将引发永久重定向错误。

http://docs.aws.amazon.com/AmazonS3/latest/dev/VirtualHosting.html

http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region

这是很多信息,但希望不仅有助于解释您需要采取哪些不同的做法,还有助于说明原因。

【讨论】:

【解决方案2】:

您的前端机器无法通过域名服务解析主机名。最可能的问题是您为要上传到的服务器使用了错误的主机名:

mybucket.name.s3-us-east-1.amazonaws.com

如果使用的主机名正确,请验证您尝试访问的主机的 DNS 信息是否可从您的客户端获得。比如linux下使用dig命令,Windows下使用nslookup。

【讨论】:

  • 主机名不正确,在这种情况下;不可能,因为主机名在 us-east-1 区域的 S3 中的工作方式 - 它与所有其他区域不同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-02
  • 2014-01-18
  • 2018-03-02
  • 1970-01-01
  • 2011-11-10
  • 2020-07-07
  • 2016-04-14
相关资源
最近更新 更多