2009-08-06 7 views
4

Ich speichere zwei Millionen Dateien in einem Amazon S3-Bucket. Es gibt eine gegebene Wurzel (l1) unten, eine Liste von Verzeichnissen unter l1 und dann enthält jedes Verzeichnis Dateien. So wird mein Eimer in etwa wie folgt aussehenVerzeichnisse auf einer bestimmten Ebene in Amazon S3 auflisten

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 

Ich mag würde, so schnell wie möglich die zweite Ebene Einträge aufzulisten, so möchte ich a1, a2, A5000 zu bekommen. Ich möchte nicht alle Schlüssel auflisten, das dauert viel länger.

Ich bin offen, um direkt die AWS api verwenden, aber ich habe bisher mit dem right_aws Juwel in Rubin spielt http://rdoc.info/projects/rightscale/right_aws

Es gibt mindestens zwei APIs in diesem Juwel, versuche ich bucket.keys mit() in das Modul S3 und incrementally_list_bucket() im Modul S3Interface. Ich kann das Präfix und das Trennzeichen zum Auflisten aller von l1/a1/* zum Beispiel setzen, aber ich kann nicht herausfinden, wie man nur die erste Ebene in l1 auflistet. Es gibt einen Eintrag: common_prefixes im Hash von incrementally_list_bucket(), aber in meiner Testprobe ist er nicht ausgefüllt.

Ist diese Operation mit der S3 API möglich?

Danke!

+1

Schauen Sie sich die S3-Dokumentation für die ListBucket Betrieb: http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html? SOAPListBucket.html. Um a1-a5000 zu erhalten, geben Sie prefix = "/ l1 /" delimeter = "/" an. Um/l1/a123/* zu erhalten, geben Sie prefix = "/ l1/a123 /" an, delimeter = "/". Haben Sie daran gedacht? –

+0

Oren, Sie haben Recht, es funktioniert jetzt. Danke vielmals. Vielleicht war die Test-Bucket-Struktur, die ich erstellt habe, falsch. –

Antwort

6

right_aws ermöglicht dies als Teil ihrer zugrunde liegenden S3Interface Klasse zu tun, aber Sie können Ihre eigene Methode für eine einfachere (und netter) verwenden. Setzen Sie dieses an der Spitze des Codes:

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 

Diese die common_prefixes Methode zum RightAws::S3::Bucket Klasse hinzufügt. Anstatt jetzt mybucket.keys aufzurufen, um die Liste der Schlüssel in Ihrem Bucket abzurufen, können Sie verwenden, um ein Array mit gemeinsamen Präfixen zu erhalten. In Ihrem Fall:

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

Ich muss sagen, ich habe es nur mit einer kleinen Anzahl von gemeinsamen Präfixen getestet; Sie sollten prüfen, ob dies mit mehr als 1000 gemeinsamen Präfixen funktioniert.

+1

Es ist erstaunlich, dass das noch nicht eingebaut wurde, aber das hat mir viel Zeit gespart.Vielen Dank. – drudru

+1

Ich habe es hier etwas verbessert: http://stackoverflow.com/questions/4849939/how-to-do-the-äquivalent-of-s3cmd-ls-s3-some-bucket-foo-bar-in-ruby/ (es listet jetzt auch einzelne Dateien auf). Ich kann immer noch nicht glauben, dass dies nicht in einer der vielen Ruby S3-Edelsteine ​​eingebaut ist. – Erik

0

Dieser Thread ist ziemlich alt, aber ich habe in dieser Frage vor kurzem laufen und wollte meine 2cents behaupten ...

Es ist ein Streit und eine Hälfte (es scheint) zu sauber Ordner auflisten gegeben einen Pfad in ein S3 Eimer. Die meisten aktuellen Gem Wrapper um die S3-API (AWS-SDK offiziell, S3) analysieren das Rückgabeobjekt (speziell die CommonPrefixes) nicht korrekt, so dass es schwierig ist, eine Liste von Ordnern (Trennzeichen-Albträume) zurück zu bekommen.

Hier ist eine schnelle Lösung für diejenigen, die das S3-Juwel verwenden ... Es tut mir leid, es ist nicht eine Größe passt für alle, aber es ist das Beste, was ich tun wollte.

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

-Code-Schnipsel:

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