在Amazon S3中列出给定级别的目录

我在亚马逊S3桶中存储了200万个文件。 下面有一个给定的根(l1),l1下的目录列表,然后每个目录包含文件。 所以我的桶看起来像下面这样

l1/a1/file1-1.jpg l1/a1/file1-2.jpg l1/a1/... another 500 files l1/a2/file2-1.jpg l1/a2/file2-2.jpg l1/a2/... another 500 files .... l1/a5000/file5000-1.jpg 

我想尽快列出第二级条目,所以我想得到a1,a2,a5000。 我不想列出所有的密钥,这将花费更长的时间。

我很乐意直接使用AWS api,但是到目前为止我已经使用了ruby中的right_aws gem http://rdoc.info/projects/rightscale/right_aws

该gem中至少有两个API,我尝试在S3模块中使用bucket.keys(),在S3Interface模块中使用incrementally_list_bucket()。 例如,我可以设置前缀和分隔符来列出所有l1 / a1 / *,但我无法弄清楚如何仅列出l1中的第一级。 在incrementally_list_bucket()返回的哈希中有一个:common_prefixes条目,但在我的测试样本中没有填写。

S3 API可以实现此操作吗?

谢谢!

right_aws允许这样做作为其底层S3Interface类的一部分,但您可以创建自己的方法,以便更容易(和更好)的使用。 把它放在代码的顶部:

 module RightAws class S3 class Bucket def common_prefixes(prefix, delimiter = '/') common_prefixes = [] @s3.interface.incrementally_list_bucket(@name, { 'prefix' => prefix, 'delimiter' => delimiter }) do |thislist| common_prefixes += thislist[:common_prefixes] end common_prefixes end end end end 

这将common_prefixes方法添加到RightAws::S3::Bucket类。 现在,您可以使用mybucket.common_prefixes来获取一系列公共前缀,而不是调用mybucket.keys来获取存储桶中的键列表。 在你的情况下:

 mybucket.common_prefixes("l1/") # => ["l1/a1", "l1/a2", ... "l1/a5000"] 

我必须说我只用少量公共前缀测试它; 你应该检查这是否适用于超过1000个公共前缀。

这个post已经很老了,但我最近遇到过这个问题,想要断言我的2cents ……

在S3存储桶中给出路径时,干净地列出文件夹是一件麻烦的事情。 S3 API(AWS-SDK官方,S3)周围的大多数当前gem包装器都没有正确解析返回对象(特别是CommonPrefixes),因此很难找回文件夹列表(分隔符噩梦)。

这是一个使用S3gem的快速修复…对不起它不是一个尺寸适合所有,但它是我想做的最好的。

https://github.com/qoobaa/s3/issues/61

代码段:

 module S3 class Bucket # this method recurses if the response coming back # from S3 includes a truncation flag (IsTruncated == 'true') # then parses the combined response(s) XML body # for CommonPrefixes/Prefix AKA directories def directory_list(options = {}, responses = []) options = {:delimiter => "/"}.merge(options) response = bucket_request(:get, :params => options) if is_truncated?(response.body) directory_list(options.merge({:marker => next_marker(response.body)}), responses << response.body) else parse_xml_array(responses + [response.body], options) end end private def parse_xml_array(xml_array, options = {}, clean_path = true) names = [] xml_array.each do |xml| rexml_document(xml).elements.each("ListBucketResult/CommonPrefixes/Prefix") do |e| if clean_path names << e.text.gsub((options[:prefix] || ''), '').gsub((options[:delimiter] || ''), '') else names << e.text end end end names end def next_marker(xml) marker = nil rexml_document(xml).elements.each("ListBucketResult/NextMarker") {|e| marker ||= e.text } if marker.nil? raise StandardError else marker end end def is_truncated?(xml) is_truncated = nil rexml_document(xml).elements.each("ListBucketResult/IsTruncated") {|e| is_truncated ||= e.text } is_truncated == 'true' end end end