Below piece of code returns ONLY the 'subfolders' in a 'folder' from s3 bucket.
import boto3
bucket = 'my-bucket'
#Make sure you provide / in the end
prefix = 'prefix-name-with-slash/'
client = boto3.client('s3')
result = client.list_objects(Bucket=bucket, Prefix=prefix, Delimiter='/')
for o in result.get('CommonPrefixes'):
print 'sub folder : ', o.get('Prefix')
For more details, you can refer to https://github.com/boto/boto3/issues/134
Answer from Dipankar on Stack OverflowBelow piece of code returns ONLY the 'subfolders' in a 'folder' from s3 bucket.
import boto3
bucket = 'my-bucket'
#Make sure you provide / in the end
prefix = 'prefix-name-with-slash/'
client = boto3.client('s3')
result = client.list_objects(Bucket=bucket, Prefix=prefix, Delimiter='/')
for o in result.get('CommonPrefixes'):
print 'sub folder : ', o.get('Prefix')
For more details, you can refer to https://github.com/boto/boto3/issues/134
S3 is an object storage, it doesn't have real directory structure. The "/" is rather cosmetic. One reason that people want to have a directory structure, because they can maintain/prune/add a tree to the application. For S3, you treat such structure as sort of index or search tag.
To manipulate object in S3, you need boto3.client or boto3.resource, e.g. To list all object
import boto3
s3 = boto3.client("s3")
all_objects = s3.list_objects(Bucket = 'bucket-name')
http://boto3.readthedocs.org/en/latest/reference/services/s3.html#S3.Client.list_objects
In fact, if the s3 object name is stored using '/' separator. The more recent version of list_objects (list_objects_v2) allows you to limit the response to keys that begin with the specified prefix.
To limit the items to items under certain sub-folders:
import boto3
s3 = boto3.client("s3")
response = s3.list_objects_v2(
Bucket=BUCKET,
Prefix ='DIR1/DIR2',
MaxKeys=100 )
Documentation
Another option is using python os.path function to extract the folder prefix. Problem is that this will require listing objects from undesired directories.
import os
s3_key = 'first-level/1456753904534/part-00014'
filename = os.path.basename(s3_key)
foldername = os.path.dirname(s3_key)
# if you are not using conventional delimiter like '#'
s3_key = 'first-level#1456753904534#part-00014'
filename = s3_key.split("#")[-1]
A reminder about boto3 : boto3.resource is a nice high level API. There are pros and cons using boto3.client vs boto3.resource. If you develop internal shared library, using boto3.resource will give you a blackbox layer over the resources used.
You can enumerate through all of the objects in the bucket, and find the "folder" (really the prefix up until the last delimiter), and build up a list of available folders:
seen = set()
s3 = boto3.client('s3')
paginator = s3.get_paginator('list_objects_v2')
for page in paginator.paginate(Bucket='bucket-name'):
for obj in page.get('Contents', []):
key = obj['Key']
folder = key[:key.rindex("/")] if '/' in key else ""
if folder not in seen:
seen.add(folder)
print(folder)
Alternatively, you could use the same basic logic of recursion that you were using, only with multiple workers, allowing you to alleviate some of the time spent waiting for replies:
def recursion_worker(bucket_name, prefix):
# Look in the bucket at the given prefix, and return a list of folders
s3 = boto3.client('s3')
paginator = s3.get_paginator('list_objects_v2')
folders = []
for page in paginator.paginate(Bucket=bucket_name, Prefix=prefix, Delimiter='/'):
for sub_prefix in page.get('CommonPrefixes', []):
folders.append(sub_prefix['Prefix'])
# S3 returns common prefixes first, if there are any objects, it
# means further pages will just return objects, so no need
# to keep going
# Note that this isn't a garuanteed order, though it's unlikely
# to change. To be 100% safe, don't do this check at the expense
# of a slower result set for large buckets
if len(page.get('Contents', [])) > 0:
break
return folders
def folders_via_recursion():
import multiprocessing
from collections import deque
bucket_name = 'example-bucket'
folders = []
# Spin up many workers, more than we have CPU cores for
# since most of the workers will be spending most
# of their time waiting for network traffic
with multiprocessing.Pool(processes=32) as pool:
pending = deque()
# Seed the workers with the first request to list objects at
# the root of the bucket
pending.append(pool.apply_async(recursion_worker, (bucket_name, "")))
while len(pending) > 0:
# Keep going while there are items to parse
temp = pending.popleft()
for cur in temp.get():
# Print out every folder, and store the result in an array
# to consume later on
print(cur)
folders.append(cur)
# And tell a free worker to list out the folder we just found
pending.append(pool.apply_async(recursion_worker, (bucket_name, cur)))
# All done, we can consume the folders array as needed, though
# do note, it's in a somewhat random order, run something like
# folders.sort()
# first if you want a stable order before looking at it
If even this fails, then your bucket is simply too big to parse in one go in a Lambda. You'll need to consider some other solution to get a list of files, like using AWS S3 Inventory to create a list of objects outside of this Lambda that it can process.
The list_objects_v2() call returns a list of all objects. The Key of each object includes the full path of the object.
Therefore, you can simply extract the paths from the Keys of all objects:
import boto3
s3_client = boto3.client('s3')
response = s3_client.list_objects_v2(Bucket='my-bucket')
# folder1/folder2/foo.txt --> folder1/folder2
paths = {object['Key'][:object['Key'].rfind('/')] for object in response['Contents'] if '/' in object['Key']}
for path in sorted(paths):
print(path)
If your bucket contains more than 1000 objects, then you will either need to loop through the results using ContinuationToken or use a paginator. See: list_objects_v2 paginator
How do you list the most recent subfolder on s3 using boto3?
How to list out all the folders inside a parent folder (or prefix) in a s3 bucket?
python - Listing contents of a bucket with boto3 - Stack Overflow
How get just a folder from s3 bucket using boto3?
Lets say that My_bucket are the bucket that are public and there are /Cupcake/Vanilla/ subfolders. how do you get the most recent created subfolder on boto3?
I need to check if a folder exists inside a parent folder in s3 bucket. Since there is no direct way, I thought of using list_objects method of boto3 to list out all the objects in a folder and then search for the folder name in the results.
But, how can I get the names of all the folders inside a parent folder (or s3 prefix) with list_objects method?
I find the usage of delimiter and prefixes to be confusing. Especially delimiter. Why there is not a simple way to list files and directories just like in the Unix system. I understand s3 is not a file system but they can provide a wrapper function for it.
One way to see the contents would be:
for my_bucket_object in my_bucket.objects.all():
print(my_bucket_object)
This is similar to an 'ls' but it does not take into account the prefix folder convention and will list the objects in the bucket. It's left up to the reader to filter out prefixes which are part of the Key name.
In Python 2:
from boto.s3.connection import S3Connection
conn = S3Connection() # assumes boto.cfg setup
bucket = conn.get_bucket('bucket_name')
for obj in bucket.get_all_keys():
print(obj.key)
In Python 3:
from boto3 import client
conn = client('s3') # again assumes boto.cfg setup, assume AWS S3
for key in conn.list_objects(Bucket='bucket_name')['Contents']:
print(key['Key'])