Node.js&Amazon S3:如何迭代桶中的所有文件?
是否有任何Node.js的Amazon S3客户端库允许列出S3存储桶中的所有文件?
最知名的aws2js和knox似乎没有这个function。
使用官方aws-sdk :
var allKeys = []; function listAllKeys(marker, cb) { s3.listObjects({Bucket: s3bucket, Marker: marker}, function(err, data){ allKeys.push(data.Contents); if(data.IsTruncated) listAllKeys(data.NextMarker, cb); else cb(); }); }
见s3.listObjects
这里是我编写的从截断列表组装S3对象的节点代码。
var params = { Bucket: <yourbucket>, Prefix: <yourprefix>, }; var s3DataContents = []; // Single array of all combined S3 data.Contents function s3Print() { if (program.al) { // --al: Print all objects console.log(JSON.stringify(s3DataContents, null, " ")); } else { // --b: Print key only, otherwise also print index var i; for (i = 0; i < s3DataContents.length; i++) { var head = !program.b ? (i+1) + ': ' : ''; console.log(head + s3DataContents[i].Key); } } } function s3ListObjects(params, cb) { s3.listObjects(params, function(err, data) { if (err) { console.log("listS3Objects Error:", err); } else { var contents = data.Contents; s3DataContents = s3DataContents.concat(contents); if (data.IsTruncated) { // Set Marker to last returned key params.Marker = contents[contents.length-1].Key; s3ListObjects(params, cb); } else { cb(); } } }); } s3ListObjects(params, s3Print);
请注意listMark的NextMarker文档,它并不总是出现在返回的数据对象中,所以在上面的代码中我完全不使用它。
NextMarker – (String)当响应被截断(响应中的IsTruncated元素值为true)时,可以使用此字段中的键名称作为后续请求中的标记以获取下一组对象。 Amazon S3按字母顺序列出对象注意:仅当您指定了分隔符请求参数时才会返回此元素。 如果响应不包括NextMarker并且它被截断,那么可以使用响应中最后一个Key的值作为后续请求中的标记以获取下一组对象密钥 。
整个程序现在已经推到https://github.com/kenklin/s3list 。
实际上, aws2js通过s3.get()
方法调用来支持低级别的对象列表。 要做到这一点,必须传递在Amazon S3 REST API页面上logging的prefix
参数:
var s3 = require('aws2js').load('s3', awsAccessKeyId, awsSecretAccessKey); s3.setBucket(bucketName); var folder = encodeURI('some/path/to/S3/folder'); var url = '?prefix=' + folder; s3.get(url, 'xml', function (error, data) { console.log(error); console.log(data); });
上面代码片段中的data
variables包含了bucketName
桶中所有对象的列表。
当我找不到一个好的现有解决scheme时发布knox-copy 。 将Rest API的所有分页细节封装到熟悉的节点stream中:
var knoxCopy = require('knox-copy'); var client = knoxCopy.createClient({ key: '<api-key-here>', secret: '<secret-here>', bucket: 'mrbucket' }); client.streamKeys({ // omit the prefix to list the whole bucket prefix: 'buckets/of/fun' }).on('data', function(key) { console.log(key); });
如果列出less于1000个文件,则单个页面将工作:
client.listPageOfKeys({ prefix: 'smaller/bucket/o/fun' }, function(err, page) { console.log(page.Contents); // <- Here's your list of files });
这是一个老问题,我想AWS JS SDK自从问了以后就发生了很大的变化。 现在还有另外一种方法可以做到这一点:
s3.listObjects({Bucket:'mybucket', Prefix:'some-pfx'}). on('success', function handlePage(r) { //... handle page of contents r.data.Contents if(r.hasNextPage()) { // There's another page; handle it r.nextPage().on('success', handlePage).send(); } else { // Finished! } }). on('error', function(r) { // Error! }). send();
Meekohi提供了一个非常好的答案,但(新)文档指出,NextMarker可以是未定义的。 在这种情况下,您应该使用最后一个键作为标记。
所以他的codesample可以改成:
var allKeys = []; function listAllKeys(marker, cb) { s3.listObjects({Bucket: s3bucket, Marker: marker}, function(err, data){ allKeys.push(data.Contents); if(data.IsTruncated) listAllKeys(data.NextMarker || data.Contents[data.Contents.length-1].Key, cb); else cb(); }); }
由于我没有所需的信誉,因此无法评论原始答案。 道歉的坏标记顺便说一句。
虽然@ Meekohi的答案在技术上是可行的,但我已经对NodeJS的AWS SDK的S3部分感到非常痛苦。 在之前所有与aws-sdk
, s3
, knox
等模块knox
,我决定通过OS包pipe理器安装child_process
,并使用child_process
就像是:
var s3cmd = new cmd_exec('s3cmd', ['ls', filepath, 's3://'+inputBucket], function (me, data) {me.stdout += data.toString();}, function (me) {me.exit = 1;} ); response.send(s3cmd.stdout);
(使用这个问题的cmd_exec
实现)
这种方法工作得很好 – 包括file upload等其他有问题的事情。
如果只想在S3 Bucket中的特定文件夹中获取密钥列表,那么这将是有用的。
基本上, listObjects
函数将从我们设置的Marker
开始search,它将search,直到maxKeys: 1000
作为限制。 所以它会search一个一个的文件夹,并获得您从一个存储桶中的不同文件夹中find的前1000个密钥。
考虑我有很多文件夹在我的桶里,前缀是prod/some date/, Ex: prod/2017/05/12/ ,prod/2017/05/13/,etc
。
我只想在prod/2017/05/12/
文件夹中获取对象列表(文件名),然后我将指定prod/2017/05/12/
作为我的开始和prod/2017/05/13/
[您的下一个文件夹名称]作为我的结束,并在代码我打破了循环,当我遇到结束。
data.Contents
每个Key
都看起来像这样。
{ Key: 'prod/2017/05/13/4bf2c675-a417-4c1f-a0b4-22fc45f99207.jpg', LastModified: 2017-05-13T00:59:02.000Z, ETag: '"630b2sdfsdfs49ef392bcc16c833004f94ae850"', Size: 134236366, StorageClass: 'STANDARD', Owner: { } }
码:
var list = []; function listAllKeys(s3bucket, start, end) { s3.listObjects({ Bucket: s3bucket, Marker: start, MaxKeys: 1000, }, function(err, data) { if (data.Contents) { for (var i = 0; i < data.Contents.length; i++) { var key = data.Contents[i].Key; //See above code for the structure of data.Contents if (key.substring(0, 19) != end) { list.push(key); } else { break; // break the loop if end arrived } } console.log(list); console.log('Total - ', list.length); } }); } listAllKeys('BucketName', 'prod/2017/05/12/', 'prod/2017/05/13/');
输出:
[ 'prod/2017/05/12/05/4bf2c675-a417-4c1f-a0b4-22fc45f99207.jpg', 'prod/2017/05/12/05/a36528b9-e071-4b83-a7e6-9b32d6bce6d8.jpg', 'prod/2017/05/12/05/bc4d6d4b-4455-48b3-a548-7a714c489060.jpg', 'prod/2017/05/12/05/f4b8d599-80d0-46fa-a996-e73b8fd0cd6d.jpg', ... 689 more items ] Total - 692
最简洁的方法是通过像我这样的节点脚本执行s3cmd(这里的例子是recursion地删除文件):
var exec = require('child_process').exec; var child; var bucket = "myBucket"; var prefix = "myPrefix"; // this parameter is optional var command = "s3cmd del -r s3://" + bucket + "/" + prefix; child = exec(command, {maxBuffer: 5000 * 1024}, function (error, stdout, stderr) { // the maxBuffer is here to avoid the maxBuffer node process error console.log('stdout: ' + stdout); if (error !== null) { console.log('exec error: ' + error); } });