【问题标题】:Listing directories at a given level in Amazon S3在 Amazon S3 中列出给定级别的目录
【发布时间】:2010-11-17 09:41:18
【问题描述】:

我在亚马逊 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 进行此操作?

谢谢!

【问题讨论】:

  • 查看 ListBucket 操作的 S3 文档:docs.amazonwebservices.com/AmazonS3/2006-03-01/…。要获得 a1-a5000,请指定前缀="/l1/" 分隔符="/"。要获取 /l1/a123/*,请指定前缀="/l1/a123/"、分隔符="/"。这就是你的想法吗?
  • Oren,你说得对,它现在正在工作。非常感谢。也许我创建的测试桶结构是错误的。

标签: ruby amazon-s3 directory


【解决方案1】:

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 多个常见前缀。

【讨论】:

  • 令人惊讶的是它还没有内置,但这为我节省了很多时间。谢谢。
  • 我在这里对其进行了一些改进:stackoverflow.com/questions/4849939/…(它现在还列出了单个文件)。仍然无法相信这不是内置在众多 Ruby S3 gem 之一中。
【解决方案2】:

这个帖子已经很老了,但我最近确实遇到了这个问题,想维护我的 2cents...

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

这里是为那些使用 S3 gem 的用户提供的快速修复...抱歉,它不是一个适合所有人的尺寸,但它是我想做的最好的。

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

代码sn-p:

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

【讨论】:

    猜你喜欢
    • 2013-07-07
    • 1970-01-01
    • 1970-01-01
    • 2018-03-15
    • 2018-03-03
    • 1970-01-01
    • 1970-01-01
    • 2010-12-15
    • 2016-02-08
    相关资源
    最近更新 更多