2009-08-06 11 views
4

Je stocke deux millions de fichiers dans un compartiment Amazon S3. Il y a une racine donnée (l1) ci-dessous, une liste de répertoires sous l1 et ensuite chaque répertoire contient des fichiers. Donc, mon seau ressemblera à quelque chose comme ce qui suitRépertorier des répertoires à un niveau donné dans Amazon S3

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 

Je voudrais à la liste le plus rapidement possible les deuxièmes entrées de niveau, donc je voudrais obtenir a1, a2, A5000. Je ne veux pas énumérer toutes les clés, cela prendra beaucoup plus de temps.

Je suis ouvert à utiliser directement le AWS api, mais je l'ai joué jusqu'à présent avec le joyau de right_aws en rubis http://rdoc.info/projects/rightscale/right_aws

Il y a au moins deux API dans ce petit bijou, j'essayé d'utiliser bucket.keys() dans le module S3 et incrementally_list_bucket() dans le module S3Interface. Je peux définir le préfixe et le délimiteur pour lister tout l1/a1/*, par exemple, mais je ne peux pas comprendre comment lister seulement le premier niveau dans l1. Il y a une entrée: common_prefixes dans le hash retourné par incrementally_list_bucket() mais dans mon exemple de test il n'est pas renseigné.

Cette opération est-elle possible avec l'API S3?

Merci!

+1

Vérifiez la documentation S3 pour l'opération de ListBucket: http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html? SOAPListBucket.html. Pour obtenir a1-a5000, spécifiez prefix = "/ l1 /" delimeter = "/". Pour obtenir/l1/a123/*, spécifiez prefix = "/ l1/a123 /", delimeter = "/". Est-ce ce que tu avais en tête? –

+0

Oren, Vous avez raison, cela fonctionne actuellement. Merci beaucoup. La structure de seau de test que j'ai créée était peut-être incorrecte. –

Répondre

6

right_aws permet de faire cela dans le cadre de leur classe S3Interface sous-jacente, mais vous pouvez créer votre propre méthode pour une utilisation plus facile (et plus agréable). Mettre cela en haut de votre code:

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 

Cela ajoute la méthode common_prefixes à la classe RightAws::S3::Bucket. Maintenant, au lieu d'appeler mybucket.keys pour récupérer la liste des clés dans votre compartiment, vous pouvez utiliser mybucket.common_prefixes pour obtenir un tableau de préfixes communs. Dans votre cas:

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

Je dois dire que je l'ai testé seulement avec un petit nombre de préfixes communs; vous devriez vérifier que cela fonctionne avec plus de 1000 préfixes communs.

+1

c'est incroyable que ça n'ait pas encore été construit, mais cela m'a fait gagner beaucoup de temps.Merci. – drudru

+1

Je l'ai amélioré un peu ici: http://stackoverflow.com/questions/4849939/how-to-do-the-equivalent-of-s3cmd-ls-s3-some-bucket-foo-bar-in-ruby/ (il répertorie également les fichiers individuels maintenant). Ne peut toujours pas croire que ce n'est pas intégré dans l'un des nombreux gemmes Ruby S3. – Erik

0

Ce fil est assez vieux, mais je ne l'ai rencontré ce problème récemment et je voulais faire valoir mes 2cents ...

Il est embêtant et demi (il semble) à la liste proprement des dossiers donné un chemin un seau S3. La plupart des wrappers actuels autour de l'API S3 (AWS-SDK officiel, S3) n'analysent pas correctement l'objet de retour (en particulier les CommonPrefix), il est donc difficile de retrouver une liste de dossiers (délimiteurs cauchemars).

Voici une solution rapide pour ceux qui utilisent la gemme S3 ... Désolé, ce n'est pas une taille unique, mais c'est le meilleur que je voulais faire.

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

extrait de code:

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 
Questions connexes