AngularJs 图片上传到S3
AngularJs Image upload to S3
我是:
- 创建 Web 应用程序
- AngularJS 带有 ng 文件上传的前端 (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
程序流程本质上就是select文件,点击一个按钮,请求后台签名,然后上传到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.
});
};
}]);
您的 front-end 机器无法通过域名服务解析主机名。最可能的问题是您为要上传到的服务器使用了错误的主机名:
mybucket.name.s3-us-east-1.amazonaws.com
如果使用的主机名正确,请验证您尝试访问的主机的 DNS 信息是否可从您的客户端获得。比如在linux下使用dig命令或者在Windows下使用nslookup.
该示例包含一些我认为过于简单化的错误。
var s3Url = 'https://' + aws.bucket + '.s3-' + aws.region + '.amazonaws.com';
这在大部分时间都有效,但它并不是 始终 为 S3 中的对象制作 URL 的有效方法。
它至少在两种情况下出现故障,其中一种是您遇到的那种。
S3的美国标准区域的端点,位于AWS的us-east-1区域, 不是 s3-us-east-1.amazonaws.com
,如果它对所有其他区域使用相同的格式就会是这样。由于似乎 legacy/evolutionary 的原因,它只是 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),第一个组件只是 s3
或 s3-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
这是很多信息,但希望不仅能解释您需要采取不同的做法,还能解释原因。
我是:
- 创建 Web 应用程序
- AngularJS 带有 ng 文件上传的前端 (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
程序流程本质上就是select文件,点击一个按钮,请求后台签名,然后上传到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.
});
};
}]);
您的 front-end 机器无法通过域名服务解析主机名。最可能的问题是您为要上传到的服务器使用了错误的主机名:
mybucket.name.s3-us-east-1.amazonaws.com
如果使用的主机名正确,请验证您尝试访问的主机的 DNS 信息是否可从您的客户端获得。比如在linux下使用dig命令或者在Windows下使用nslookup.
该示例包含一些我认为过于简单化的错误。
var s3Url = 'https://' + aws.bucket + '.s3-' + aws.region + '.amazonaws.com';
这在大部分时间都有效,但它并不是 始终 为 S3 中的对象制作 URL 的有效方法。
它至少在两种情况下出现故障,其中一种是您遇到的那种。
S3的美国标准区域的端点,位于AWS的us-east-1区域, 不是 s3-us-east-1.amazonaws.com
,如果它对所有其他区域使用相同的格式就会是这样。由于似乎 legacy/evolutionary 的原因,它只是 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),第一个组件只是 s3
或 s3-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
这是很多信息,但希望不仅能解释您需要采取不同的做法,还能解释原因。