使用签名的 url 将文件从 angularjs 直接上传到 amazon s3
Posted
技术标签:
【中文标题】使用签名的 url 将文件从 angularjs 直接上传到 amazon s3【英文标题】:upload file from angularjs directly to amazon s3 using signed url 【发布时间】:2013-10-19 19:16:03 【问题描述】:所以我在将文件直接上传到 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 控制器尝试使用该签名 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
我知道在亚马逊端为该存储桶正确设置了 S3 CORS,因为我开发了使用相同存储桶进行开发存储的 ruby 应用程序。 (当然我用回形针和雾来做这些)。其次,由于我没有亚马逊响应的失败捕获,我不怀疑错误来自那里。但是,它确实来自我尝试将文件放在亚马逊上的那一行。
所以我确定我遗漏了一些东西,但我认为使用签名 URL 时,我只需要对那个 URL 进行放置即可。
【问题讨论】:
只是好奇......你有没有在 Angular 中得到这个工作?我正准备做类似的事情。 从来没有让它工作。最终上传到我的 node.js 服务器并使用亚马逊的 node 包上传。 @nbppp2 您是否尝试过使用 ng-s3upload 和 rails 应用程序进行文件上传? 所以我正在开发的应用程序是在 Node 中而不是在 Rails 中,但是在 nodejs 中下面的 rails 示例代码有等价物。可悲的是,在给出答案时,我们已经以不同的方式实现了它。由于它为我们提供了其他好处,我们决定保持这种不同的方式。我确实回去尝试了这个模块,并且能够让它在 Angular 和 nodejs 中工作。但是我回去看看我是否能找到我的代码,但似乎我删除了它。可能在我认为我不会再去的本地分支上。 【参考方案1】:我一直在努力解决这个问题,终于弄明白了!我将详细说明我的步骤,希望它可以帮助一些人。
我用过这个模块:https://github.com/asafdav/ng-s3upload
我按照他们列出的步骤进行操作,即:
-
创建存储桶
授予“放置/删除”权限:展开“权限”部分并点击“添加更多权限”按钮。选择“所有人”和“上传/删除”并保存。
添加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 - AWS 所需的策略 json,base64 编码。 YYY - 您的私钥的 HMAC 和 sha 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 不允许Origin 'localhost:3000'
从您的错误来看,您似乎没有在 AWS 端设置 CORS 规则。
【讨论】:
嗨,我也在尝试使用 ng-S3upload,你能告诉我应该在 s3-upload-options="getOptionsUri: 's3OptionsUri' 中输入什么吗? 这是您将 url 放入您创建的服务的位置,这将为 ng-s3Upload 提供 "policy":"XXXX", "signature":"YYY", "key": "ZZZ" 对象如上所述,例如:您将有 options="getOptionsUri: '/api/getSig'" 假设您的服务在 /api/getSig 完整示例 非常感谢!目前,我面临签名不匹配的错误,你能帮我吗?我遵循 ng-s3upload,但收到此错误。你以前有过这个问题吗? 我的签名是由策略和秘密访问密钥生成的,公钥是亚马逊提供的访问密钥id,对吗? 是的,访问密钥是亚马逊提供的您的公钥。您永远不会希望以明文形式传输您的密钥,这将是一个重大的安全漏洞,这就是您使用您的私钥“签署”要通过网络传输的数据的原因。在 aws 方面,亚马逊还可以在给定公钥的情况下使用您的密钥签署您发送的策略,并查看结果是否匹配。【参考方案2】:这个例子可能会有所帮助: https://github.com/bookingbricks/file-upload-example 使用:节点、aws-sdk-js、jQuery-file-upload (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');
);
【讨论】:
虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效。以上是关于使用签名的 url 将文件从 angularjs 直接上传到 amazon s3的主要内容,如果未能解决你的问题,请参考以下文章
使用 Filesystem Laravel 5.2 从 amazon s3 获取文件的签名 URL
如何使用 angularjs 从 url 下载(保存)图像到我们的相册中?