18

我试图建立一个允许用户从NodeJS支持的网站直接上传文件到我的Amazon S3存储桶的文件。它似乎是唯一的教程,除了the actual amazon docs for this都非常过时。亚马逊S3 POST API,并使用NodeJS签署策略

我一直在关注this tutorial,对于基本信息,但它又是过时的。它没有正确调用crypto的方法,因为它试图将原始JavaScript对象传递给update方法,该方法由于不是字符串或缓冲区而引发错误。

我也一直在寻找来源the knox npm package。它没有内置POST支持 - 我完全理解,因为它是一旦浏览器拥有正确的字段,就会执行POST。诺克斯似乎有正确的代码来签署一项政策,并且我试图让我的代码在此基础上工作......但再次无济于事。

这是我想出的代码。它产生一个base64编码策略,并创建一个签名......但是当我尝试进行文件上传时,它是根据Amazon的错误签名。


var crypto = require("crypto"); 
var config = require("../../amazonConfig.json"); 

exports.createS3Policy = function(callback) { 
    var date = new Date(); 

    var s3Policy = { 
    "expiration": "2014-12-01T12:00:00.000Z", 
    "conditions": [ 
     {"acl": "public-read"}, 
     ["content-length-range", 0, 2147483648], 
     {"bucket": "signalleaf"}, 
     ["starts-with", "$Cache-Control", ""], 
     ["starts-with", "$Content-Type", ""], 
     ["starts-with", "$Content-Disposition", ""], 
     ["starts-with", "$Content-Encoding", ""], 
     ["starts-with", "$Expires", ""], 
     ["starts-with", "$key", "/myfolder/"], 
     {"success_action_redirect": "http://example.com/uploadsuccess"}, 
    ] 
    }; 

    var stringPolicy = JSON.stringify(s3Policy).toString("utf-8"); 
    var buffer = Buffer(stringPolicy, "utf-8"); 

    var encoded = buffer.toString("base64"); 
    var signature = crypto.createHmac("sha1", config.secretKey) 
    .update(new Buffer(stringPolicy, "utf-8")).digest("base64"); 


    var s3Credentials = { 
    s3PolicyBase64: encoded, 
    s3Signature: signature 
    }; 

    GLOBAL.s3creds = s3Credentials; 

    callback(s3Credentials); 
}; 

我明显做错了,在这里。但我不知道是什么。任何人都可以帮助确定我做错了什么?我的问题在哪里? 有没有人有关于如何生成适当的Amazon S3策略的工作教程,其中包含来自NodeJS v0.10.x的签名,用于POST s3 REST API?

+1

直接上传文件至S3是不是一个真正的简单的任务,特别是如果你想支持分块,自动恢复,用户的元数据,等等,等等政策的东西_can_相当复杂。考虑使用我维护的库:[Fine Uploader](http://fineuploader.com)。它支持在所有浏览器上直接上传到S3,甚至IE7。分块和自动恢复等功能也受支持。此外,我自己写了一个[node.js服务器端示例](http://bit.ly/1do27a0),与Fine Uploader S3配对时,将为您处理所有签名。 –

+0

你能发表该评论作为答案吗?我可能会最终使用你的图书馆。仍然在评估它是如何工作的,等等。 –

+1

我不确定那会很好。坦率地说,这可能被认为是一个糟糕的或仅仅是连接的答案。我的理解是,社区正在寻找包含代码的详细答案,而我的不符合该说明,这就是为什么我将其作为评论发布的原因。如果您对Fine Uploader有任何疑问,可以查看SO上的fine-uploader标签,在那里我们处理图书馆的支持问题。 –

回答

34

好吧,我终于明白了。玩随机猜谜游戏了很长一段时间之后,我心想

“也许我需要签署base64编码的政策” - 我

BAM就是这样。

我也重新命令条件来匹配表单的发布方式,但我不确定这是否有所作为。

var crypto = require("crypto"); 
var config = require("../../amazonConfig.json"); 

exports.createS3Policy = function(contentType, callback) { 
    var date = new Date(); 

    var s3Policy = { 
    "expiration": "2014-12-01T12:00:00.000Z", // hard coded for testing 
    "conditions": [ 
     ["starts-with", "$key", "somefolder/"], 
     {"bucket": "my-bucket-name"}, 
     {"acl": "public-read"}, 
     ["starts-with", "$Content-Type", contentType], 
     {"success_action_redirect": "http://example.com/uploadsuccess"}, 
    ] 
    }; 

    // stringify and encode the policy 
    var stringPolicy = JSON.stringify(s3Policy); 
    var base64Policy = Buffer(stringPolicy, "utf-8").toString("base64"); 

    // sign the base64 encoded policy 
    var signature = crypto.createHmac("sha1", config.secretKey) 
    .update(new Buffer(base64Policy, "utf-8")).digest("base64"); 

    // build the results object 
    var s3Credentials = { 
    s3Policy: base64Policy, 
    s3Signature: signature 
    }; 

    // send it back 
    callback(s3Credentials); 
}; 

希望这可以帮助其他人解决同样的问题。

+0

谢谢!此代码帮助我。一些快速评论:要格式化我使用[date.js](http://momentjs.com/docs/)的日期,如下所示:'moment.utc(expirationDate).format('YYYY-MM-DD')+' T '+ moment.utc(EXPIRATIONDATE).format(' HH:MM:SS.SSS ')+' Z''。对于缓冲区'utf8'(注意:没有连字符)是默认编码,所以我认为“utf-8”是不正确的和无关的。 – Zugwalt

+4

@Zugwalt,你可以通过格式化内容来简化这一点。 'moment.utc(expirationDate).toISOString()' – Jonathan

+0

@Jonathan更好!谢谢! – Zugwalt

14

我修改了一点前面的例子,因为它不适用于我:amazon返回了有关破坏签名的错误。

下面是如何签名应基于浏览器的上传使用POST被创建(AWS签名版本4)

http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-authentication-HTTPPOST.html

Calculating a Signature

var CryptoJS = require("crypto-js"); 

var accessKeyID = "PUT YOUR DATA"; 
var secretAccessKey = "PUT YOUR DATA"; 

var bucket = "PUT YOUR BUCKET NAME"; 
var region = "eu-central-1"; // overwrite with your region 
var folder = "users/"; // overwrite with your folder 
var expiration = "2015-09-28T12:00:00.000Z"; // overwrite date 
var date = "20150927"; // overwrite date 
var serviceName = "s3"; 


function getSignatureKey(key, dateStamp, regionName, serviceName) { 
    var kDate = CryptoJS.HmacSHA256(dateStamp, "AWS4" + key); 
    var kRegion = CryptoJS.HmacSHA256(regionName, kDate); 
    var kService = CryptoJS.HmacSHA256(serviceName, kRegion); 
    var kSigning = CryptoJS.HmacSHA256("aws4_request", kService); 

    return kSigning; 
} 

var s3Policy = {"expiration": expiration, 
    "conditions": [ 
    {"bucket": bucket}, 
    ["starts-with", "$key", folder], 
    {"acl": "public-read"}, 
    ["starts-with", "$Content-Type", "image/"], 
    {"x-amz-meta-uuid": "14365123651274"}, 
    ["starts-with", "$x-amz-meta-tag", ""], 
    {"x-amz-credential": accessKeyID + "/" + date + "/" + region + "/" + serviceName +"/aws4_request"}, 
    {"x-amz-algorithm": "AWS4-HMAC-SHA256"}, 
    {"x-amz-date": date + "T000000Z" } 
    ] 
}; 

var base64Policy = new Buffer(JSON.stringify(s3Policy), "utf-8").toString("base64"); 
console.log('base64Policy:', base64Policy); 

var signatureKey = getSignatureKey(secretAccessKey, date, region, serviceName); 
var s3Signature = CryptoJS.HmacSHA256(base64Policy, signatureKey).toString(CryptoJS.enc.Hex); 
console.log('s3Signature:', s3Signature); 

接着生成base64Policy和s3Signature我用在上传的表单。 示例在这里: http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html

非常重要的是检查您在html表单和您的策略中是否具有相同的字段和值。

1

我继续有问题,所以我通过他们的工作,并张贴在这里我的解决方案:

https://github.com/nikkwong/ng2-s3-uploader

总之,如果你在构建签名scabbiaza的答案去,一定要建立形式像这样:

let formData = new FormData; 
formData.append('acl', xAmzAcl); 
formData.append('Content-Type', file.type); 
formData.append('X-Amz-Date', xAmzDate); 
formData.append('x-amz-server-side-encryption', xAmzServerSideEncryption); 
formData.append('x-amz-meta-uuid', xAmzMetaUuid); 
formData.append('X-Amz-Algorithm', xAmzAlgorithm); 
formData.append('X-Amz-Credential', xAmzCredential); 
formData.append('X-Amz-Signature', s3Signature); 
formData.append('Policy', base64Policy); 
formData.append('key', folder + '/' + file.name); 
// File field must come last! 
formData.append('file', file); 
+0

您的表单数据不包含“x-amz-meta-tag”。 –

+1

无论如何,我仍会检查。 –