Список каталогов на заданном уровне в Amazon S3 - PullRequest
4 голосов
/ 06 августа 2009

Я храню два миллиона файлов в корзине Amazon S3. Ниже указан корень (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. Я не хочу перечислять все ключи, это займет намного больше времени.

Я открыт для непосредственного использования API AWS, однако я до сих пор играл с гемом right_aws в ruby ​​http://rdoc.info/projects/rightscale/right_aws

В этом геме есть как минимум два API, я попытался использовать bucket.keys () в модуле S3 и incrementally_list_bucket () в модуле S3Interface. Например, я могу установить префикс и разделитель для перечисления всех l1 / a1 / *, но не могу понять, как перечислить только первый уровень в l1. В хэше есть запись: common_prefixes, возвращаемая incrementally_list_bucket (), но в моем тестовом примере она не заполнена.

Возможна ли эта операция с S3 API?

Спасибо!

Ответы [ 2 ]

6 голосов
/ 19 августа 2009

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.keys для получения списка ключей в вашем сегменте вы можете использовать mybucket.common_prefixes, чтобы получить массив общих префиксов. В вашем случае:

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

Должен сказать, что я тестировал его только с небольшим количеством общих префиксов; Вы должны убедиться, что это работает с более чем 1000 общих префиксов.

0 голосов
/ 09 февраля 2017

Эта ветка довольно старая, но я недавно столкнулся с этой проблемой и хотел заявить о своих 2cents ...

Полное хлопот (кажется), чтобы аккуратно перечислить папки, заданные путем в корзине S3. Большинство современных оболочек гемов вокруг S3 API (официальный AWS-SDK, S3) неправильно анализируют возвращаемый объект (в частности CommonPrefixes), поэтому трудно получить список папок (кошмары с разделителями).

Вот быстрое решение для тех, кто использует драгоценный камень S3 ... Извините, это не один размер подходит всем, но это лучшее, что я хотел сделать.

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...