359

How can I see what's inside a bucket in S3 with boto3? (i.e. do an "ls")?

Doing the following:

import boto3
s3 = boto3.resource('s3')
my_bucket = s3.Bucket('some/path/')

returns:

s3.Bucket(name='some/path/')

How do I see its contents?

John Rotenstein
  • 241,921
  • 22
  • 380
  • 470
Amelio Vazquez-Reina
  • 91,494
  • 132
  • 359
  • 564
  • 3
    use ## list_content def list_content (self, bucket_name): content = self.s3.list_objects_v2(Bucket=bucket_name) print(content) Other version is depreciated. – Denis Oct 27 '20 at 23:53

21 Answers21

393

One way to see the contents would be:

for my_bucket_object in my_bucket.objects.all():
    print(my_bucket_object)
Willem van Ketwich
  • 5,666
  • 7
  • 49
  • 57
garnaat
  • 44,310
  • 7
  • 123
  • 103
  • 2
    can i fetch the keys under particular path in bucket or with particular delimiter using boto3?? – Rahul KP Dec 14 '15 at 11:44
  • 183
    You should be able to say ``mybucket.objects.filter(Prefix='foo/bar')`` and it will only list objects with that prefix. You can also pass a ``Delimiter`` parameter. – garnaat Dec 14 '15 at 12:53
  • 5
    not working with boto3 AttributeError: 'S3' object has no attribute 'objects' – Shek Jun 30 '17 at 17:45
  • 3
    @garnaat Your comment mentioning that filter method really helped me (my code ended up much simpler and faster) - thank you! – Edward Dixon Aug 02 '17 at 16:25
  • 30
    I would advise against using `object` as a variable name as it will shadow the global type `object`. – oliland May 08 '18 at 10:28
  • 1
    This answer recursively lists all files (ie, it expands subdirectories). The question asks to do the equivalent of 'ls', which only lists immediate children. How can we just list immediate children, without recursion? – maurera Jan 14 '20 at 17:04
  • 1
    @maurera you can try using the client instead of resource. from boto3.session import Session s3client = session.client('s3') resp = s3client.list_objects_v2(Bucket='bucket-name', Prefix='foo/bar', Delimiter="/")` [x['Prefix'] for x in resp['CommonPrefixes']] – piedpiper Aug 04 '20 at 21:01
  • S3 does not have subdirectories. It has keys that can have common prefixes. You can filter based on the prefix, though as in the second comment given above from @garnaat. – UtahJarhead Mar 09 '21 at 20:35
158

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'])
Amelio Vazquez-Reina
  • 91,494
  • 132
  • 359
  • 564
cgseller
  • 3,875
  • 2
  • 19
  • 21
  • 63
    If you want to use the prefix as well, you can do it like this: `conn.list_objects(Bucket='bucket_name', Prefix='prefix_string')['Contents']` – markonovak Mar 21 '16 at 13:14
  • 37
    This only lists the first 1000 keys. From the docstring: "Returns some or all (up to 1000) of the objects in a bucket." Also, it is recommended that you use list_objects_v2 instead of list_objects (although, this also only returns the first 1000 keys). – Brett Widmeier Mar 21 '18 at 14:18
  • 8
    This limitation should be dealt with using [Paginators](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/paginators.html) – v25 Mar 20 '19 at 19:21
  • @markonovak crashes horribly if there are *none*. Means I need to leave a sentinel in there. – mckenzm Dec 15 '21 at 03:06
  • @markonovak yes, there is no exception handling in the example above for brevity and simplicity for the reader. – cgseller Dec 28 '21 at 14:38
96

I'm assuming you have configured authentication separately.

import boto3
s3 = boto3.resource('s3')

my_bucket = s3.Bucket('bucket_name')

for file in my_bucket.objects.all():
    print(file.key)
urban
  • 5,392
  • 3
  • 19
  • 45
Tushar Niras
  • 3,654
  • 2
  • 22
  • 24
84

My s3 keys utility function is essentially an optimized version of @Hephaestus's answer:

import boto3


s3_paginator = boto3.client('s3').get_paginator('list_objects_v2')


def keys(bucket_name, prefix='/', delimiter='/', start_after=''):
    prefix = prefix.lstrip(delimiter)
    start_after = (start_after or prefix) if prefix.endswith(delimiter) else start_after
    for page in s3_paginator.paginate(Bucket=bucket_name, Prefix=prefix, StartAfter=start_after):
        for content in page.get('Contents', ()):
            yield content['Key']

In my tests (boto3 1.9.84), it's significantly faster than the equivalent (but simpler) code:

import boto3


def keys(bucket_name, prefix='/', delimiter='/'):
    prefix = prefix.lstrip(delimiter)
    bucket = boto3.resource('s3').Bucket(bucket_name)
    return (_.key for _ in bucket.objects.filter(Prefix=prefix))

As S3 guarantees UTF-8 binary sorted results, a start_after optimization has been added to the first function.

Sean Summers
  • 2,514
  • 19
  • 26
  • 7
    This is by far the best answer. I was just modifying @Hephaestus's answer (because it was the highest) when I scrolled down. This should be the accepted answer and should get extra points for being concise. I would add that the generator from the second code needs to be wrapped in `list()` to return a list of files. – Richard D Oct 03 '19 at 20:34
  • 6
    @RichardD both results return generators. Many buckets I target with this code have more keys than the memory of the code executor can handle at once (eg, AWS Lambda); I prefer consuming the keys as they are generated. – Sean Summers Oct 04 '19 at 22:15
  • 1
    This is a nice solution, and you can add `Delimiter=delimiter` to the `s3_paginator.paginate` call to avoid recursing into "subdirectories." – teu Dec 30 '21 at 21:41
54

In order to handle large key listings (i.e. when the directory list is greater than 1000 items), I used the following code to accumulate key values (i.e. filenames) with multiple listings (thanks to Amelio above for the first lines). Code is for python3:

    from boto3  import client
    bucket_name = "my_bucket"
    prefix      = "my_key/sub_key/lots_o_files"

    s3_conn   = client('s3')  # type: BaseClient  ## again assumes boto.cfg setup, assume AWS S3
    s3_result =  s3_conn.list_objects_v2(Bucket=bucket_name, Prefix=prefix, Delimiter = "/")

    if 'Contents' not in s3_result:
        #print(s3_result)
        return []

    file_list = []
    for key in s3_result['Contents']:
        file_list.append(key['Key'])
    print(f"List count = {len(file_list)}")

    while s3_result['IsTruncated']:
        continuation_key = s3_result['NextContinuationToken']
        s3_result = s3_conn.list_objects_v2(Bucket=bucket_name, Prefix=prefix, Delimiter="/", ContinuationToken=continuation_key)
        for key in s3_result['Contents']:
            file_list.append(key['Key'])
        print(f"List count = {len(file_list)}")
    return file_list
Hephaestus
  • 4,337
  • 5
  • 35
  • 48
  • For pulling 220k files, this was only a couple seconds faster for me over the other option in this thread, using the for loop: " `for file in my_bucket.objects.all()` " – grantr Jun 30 '23 at 21:49
40

If you want to pass the ACCESS and SECRET keys (which you should not do, because it is not secure):

from boto3.session import Session

ACCESS_KEY='your_access_key'
SECRET_KEY='your_secret_key'

session = Session(aws_access_key_id=ACCESS_KEY,
                  aws_secret_access_key=SECRET_KEY)
s3 = session.resource('s3')
your_bucket = s3.Bucket('your_bucket')

for s3_file in your_bucket.objects.all():
    print(s3_file.key)
rjurney
  • 4,824
  • 5
  • 41
  • 62
Erwin Alberto
  • 1,064
  • 10
  • 7
  • 17
    This is less secure than having a credentials file at ~/.aws/credentials. Though it is a valid solution. – nu everest Dec 27 '17 at 00:32
  • 7
    This would require committing secrets to source control. Not good. – Jan Groth Dec 07 '18 at 01:41
  • 3
    This answer adds nothing regarding the API / mechanics of listing objects while adding a non relevant authentication method which is common for all boto resources and is a bad practice security wise – Froyke May 23 '19 at 16:00
  • 1
    What if the keys were supplied by key/secret management system like Vault (Hashicorp) - wouldn't that be better than just placing credentials file at ~/.aws/credentials ? – SunnyAk Sep 20 '19 at 20:03
  • 1
    The keys should be stored as env variables and loaded from there. They would then not be in source control. – getup8 Jul 10 '21 at 06:29
16
#To print all filenames in a bucket
import boto3

s3 = boto3.client('s3')

def get_s3_keys(bucket):

    """Get a list of keys in an S3 bucket."""
    resp = s3.list_objects_v2(Bucket=bucket)
    for obj in resp['Contents']:
      files = obj['Key']
    return files

  
filename = get_s3_keys('your_bucket_name')

print(filename)

#To print all filenames in a certain directory in a bucket
import boto3

s3 = boto3.client('s3')

def get_s3_keys(bucket, prefix):

    """Get a list of keys in an S3 bucket."""
    resp = s3.list_objects_v2(Bucket=bucket, Prefix=prefix)
    for obj in resp['Contents']:
      files = obj['Key']
      print(files)
    return files

  
filename = get_s3_keys('your_bucket_name', 'folder_name/sub_folder_name/')

print(filename)

Update: The most easiest way is to use awswrangler

import awswrangler as wr
wr.s3.list_objects('s3://bucket_name')
iselim
  • 309
  • 3
  • 8
  • 1
    Both "get_s3_keys" returns only last key. – Alexey Vazhnov Feb 28 '20 at 08:28
  • This lists all the files in the bucket though; the question was how to do an `ls`. How would you do that..only print the files in the root – Herman Jul 03 '20 at 12:30
  • 2
    it returns last key, use this: `def get_s3_keys(bucket, prefix): resp = s3.list_objects_v2(Bucket=bucket, Prefix=prefix) return [obj['Key'] for obj in resp['Contents']]` – a.k Jul 04 '21 at 12:36
  • Please help me to rename multiple files that is generated from AWS Glue for one job. I am able to rename one file but not more than one. Below is the code i tried to rename multiple files in 1 bucket: ```s3 = boto3.resource('s3') bucket_raw = s3.Bucket(bucket_name_raw) PREFIX = f"transactions/redshift/dim_member_group/{str_current_date}/dim_member_group_{str_current_datetime}.csv" for obj in bucket_raw.objects.all(): key = obj.key copy_source = {'Bucket' : bucket_raw, 'Key': key} s3.meta.client.copy(copy_source, bucket_raw, PREFIX) s3.Object(bucket_raw, key).delete()``` – Jomy Aug 22 '23 at 04:27
12
import boto3
s3 = boto3.resource('s3')

## Bucket to use
my_bucket = s3.Bucket('city-bucket')

## List objects within a given prefix
for obj in my_bucket.objects.filter(Delimiter='/', Prefix='city/'):
  print obj.key

Output:

city/pune.csv
city/goa.csv
vj sreenivasan
  • 1,283
  • 13
  • 15
10

A more parsimonious way, rather than iterating through via a for loop you could also just print the original object containing all files inside your S3 bucket:

session = Session(aws_access_key_id=aws_access_key_id,aws_secret_access_key=aws_secret_access_key)
s3 = session.resource('s3')
bucket = s3.Bucket('bucket_name')

files_in_s3 = bucket.objects.all() 
#you can print this iterable with print(list(files_in_s3))
petezurich
  • 9,280
  • 9
  • 43
  • 57
Daniel Vieira
  • 461
  • 5
  • 19
  • 7
    @petezurich , can you please explain why such a petty edit of my answer - replacing an “a” with a capital “A” at the beginning of my answer brought down my reputation by -2 , however I reckon both you and I can agree that not only is your correction NOT Relevant at all, but actually rather petty, wouldn’t you say so? Please focus on the content rather than childish revisions , most obliged ol’boy – Daniel Vieira Aug 23 '18 at 13:41
  • These were two different interactions. 1. I edited your answer which is recommended even for minor misspellings. I agree, that the boundaries between minor and trivial are ambiguous. I do not downvote any post because I see errors and I didn't in this case. I simply fix all the errors that I see. – petezurich Aug 23 '18 at 14:26
  • 2. I downvoted your answer because you wrote that `files_in_s3`is a "list object". There is no such thing in Python. It rather is an iterable and I couldn't make your code work and therefore downvoted. Than I found the error and saw your point but couldn't undo my downvote. – petezurich Aug 23 '18 at 14:29
  • 8
    @petezurich no problem , understood your , point , just one thing, in Python a list IS an object because pretty much everything in python is an object , then it also follows that a list is also an iterable, but first and foremost , it’s an object! that is why I did not understand your downvote- you were down voting something that was correct and code that works. Anyway , thanks for your apology and all the best – Daniel Vieira Aug 23 '18 at 19:02
  • 6
    @petezurich Everything in Python is an object. "List object" is completely acceptable. – Zach Garwood Feb 10 '20 at 23:18
7

So you're asking for the equivalent of aws s3 ls in boto3. This would be listing all the top level folders and files. This is the closest I could get; it only lists all the top level folders. Surprising how difficult such a simple operation is.

import boto3

def s3_ls():
  s3 = boto3.resource('s3')
  bucket = s3.Bucket('example-bucket')
  result = bucket.meta.client.list_objects(Bucket=bucket.name,
                                           Delimiter='/')
  for o in result.get('CommonPrefixes'):
    print(o.get('Prefix'))
Herman
  • 750
  • 1
  • 10
  • 23
7

One way that I used to do this:

import boto3
s3 = boto3.resource('s3')
bucket=s3.Bucket("bucket_name")
contents = [_.key for _ in bucket.objects.all() if "subfolders/ifany/" in _.key]
Rajiv Patki
  • 71
  • 1
  • 2
  • [usually](https://stackoverflow.com/a/5893946/1540468) `_` is used as a variable name when the value is to be ignored. – Paul Rooney Dec 07 '22 at 00:57
6

ObjectSummary:

There are two identifiers that are attached to the ObjectSummary:

  • bucket_name
  • key

boto3 S3: ObjectSummary

More on Object Keys from AWS S3 Documentation:

Object Keys:

When you create an object, you specify the key name, which uniquely identifies the object in the bucket. For example, in the Amazon S3 console (see AWS Management Console), when you highlight a bucket, a list of objects in your bucket appears. These names are the object keys. The name for a key is a sequence of Unicode characters whose UTF-8 encoding is at most 1024 bytes long.

The Amazon S3 data model is a flat structure: you create a bucket, and the bucket stores objects. There is no hierarchy of subbuckets or subfolders; however, you can infer logical hierarchy using key name prefixes and delimiters as the Amazon S3 console does. The Amazon S3 console supports a concept of folders. Suppose that your bucket (admin-created) has four objects with the following object keys:

Development/Projects1.xls

Finance/statement1.pdf

Private/taxdocument.pdf

s3-dg.pdf

Reference:

AWS S3: Object Keys

Here is some example code that demonstrates how to get the bucket name and the object key.

Example:

import boto3
from pprint import pprint

def main():

    def enumerate_s3():
        s3 = boto3.resource('s3')
        for bucket in s3.buckets.all():
             print("Name: {}".format(bucket.name))
             print("Creation Date: {}".format(bucket.creation_date))
             for object in bucket.objects.all():
                 print("Object: {}".format(object))
                 print("Object bucket_name: {}".format(object.bucket_name))
                 print("Object key: {}".format(object.key))

    enumerate_s3()


if __name__ == '__main__':
    main()
Peter Girnus
  • 2,673
  • 1
  • 19
  • 24
6

Here is a simple function that returns you the filenames of all files or files with certain types such as 'json', 'jpg'.

def get_file_list_s3(bucket, prefix="", file_extension=None):
            """Return the list of all file paths (prefix + file name) with certain type or all
            Parameters
            ----------
            bucket: str
                The name of the bucket. For example, if your bucket is "s3://my_bucket" then it should be "my_bucket"
            prefix: str
                The full path to the the 'folder' of the files (objects). For example, if your files are in 
                s3://my_bucket/recipes/deserts then it should be "recipes/deserts". Default : ""
            file_extension: str
                The type of the files. If you want all, just leave it None. If you only want "json" files then it
                should be "json". Default: None       
            Return
            ------
            file_names: list
                The list of file names including the prefix
            """
            import boto3
            s3 = boto3.resource('s3')
            my_bucket = s3.Bucket(bucket)
            file_objs =  my_bucket.objects.filter(Prefix=prefix).all()
            file_names = [file_obj.key for file_obj in file_objs if file_extension is not None and file_obj.key.split(".")[-1] == file_extension]
            return file_names
gench
  • 1,063
  • 1
  • 11
  • 17
4

Here is the solution

import boto3

s3=boto3.resource('s3')
BUCKET_NAME = 'Your S3 Bucket Name'
allFiles = s3.Bucket(BUCKET_NAME).objects.all()
for file in allFiles:
    print(file.key)
Thomas
  • 3,119
  • 2
  • 16
  • 22
3

I just did it like this, including the authentication method:

s3_client = boto3.client(
                's3',
                aws_access_key_id='access_key',
                aws_secret_access_key='access_key_secret',
                config=boto3.session.Config(signature_version='s3v4'),
                region_name='region'
            )

response = s3_client.list_objects(Bucket='bucket_name', Prefix=key)
if ('Contents' in response):
    # Object / key exists!
    return True
else:
    # Object / key DOES NOT exist!
    return False
Milean
  • 878
  • 9
  • 16
2

With little modification to @Hephaeastus 's code in one of the above comments, wrote the below method to list down folders and objects (files) in a given path. Works similar to s3 ls command.

from boto3 import session

def s3_ls(profile=None, bucket_name=None, folder_path=None):
    folders=[]
    files=[]
    result=dict()
    bucket_name = bucket_name
    prefix= folder_path
    session = boto3.Session(profile_name=profile)
    s3_conn   = session.client('s3')
    s3_result =  s3_conn.list_objects_v2(Bucket=bucket_name, Delimiter = "/", Prefix=prefix)
    if 'Contents' not in s3_result and 'CommonPrefixes' not in s3_result:
        return []

    if s3_result.get('CommonPrefixes'):
        for folder in s3_result['CommonPrefixes']:
            folders.append(folder.get('Prefix'))

    if s3_result.get('Contents'):
        for key in s3_result['Contents']:
            files.append(key['Key'])

    while s3_result['IsTruncated']:
        continuation_key = s3_result['NextContinuationToken']
        s3_result = s3_conn.list_objects_v2(Bucket=bucket_name, Delimiter="/", ContinuationToken=continuation_key, Prefix=prefix)
        if s3_result.get('CommonPrefixes'):
            for folder in s3_result['CommonPrefixes']:
                folders.append(folder.get('Prefix'))
        if s3_result.get('Contents'):
            for key in s3_result['Contents']:
                files.append(key['Key'])

    if folders:
        result['folders']=sorted(folders)
    if files:
        result['files']=sorted(files)
    return result

This lists down all objects / folders in a given path. Folder_path can be left as None by default and method will list the immediate contents of the root of the bucket.

ram
  • 133
  • 6
2

It can also be done as follows:

csv_files = s3.list_objects_v2(s3_bucket_path)
    for obj in csv_files['Contents']:
        key = obj['Key']
KayV
  • 12,987
  • 11
  • 98
  • 148
1

Using cloudpathlib

cloudpathlib provides a convenience wrapper so that you can use the simple pathlib API to interact with AWS S3 (and Azure blob storage, GCS, etc.). You can install with pip install "cloudpathlib[s3]".

Like with pathlib you can use glob or iterdir to list the contents of a directory.

Here's an example with a public AWS S3 bucket that you can copy and past to run.

from cloudpathlib import CloudPath

s3_path = CloudPath("s3://ladi/Images/FEMA_CAP/2020/70349")

# list items with glob
list(
    s3_path.glob("*")
)[:3]
#> [ S3Path('s3://ladi/Images/FEMA_CAP/2020/70349/DSC_0001_5a63d42e-27c6-448a-84f1-bfc632125b8e.jpg'),
#>   S3Path('s3://ladi/Images/FEMA_CAP/2020/70349/DSC_0002_a89f1b79-786f-4dac-9dcc-609fb1a977b1.jpg'),
#>   S3Path('s3://ladi/Images/FEMA_CAP/2020/70349/DSC_0003_02c30af6-911e-4e01-8c24-7644da2b8672.jpg')]

# list items with iterdir
list(
    s3_path.iterdir()
)[:3]
#> [ S3Path('s3://ladi/Images/FEMA_CAP/2020/70349/DSC_0001_5a63d42e-27c6-448a-84f1-bfc632125b8e.jpg'),
#>   S3Path('s3://ladi/Images/FEMA_CAP/2020/70349/DSC_0002_a89f1b79-786f-4dac-9dcc-609fb1a977b1.jpg'),
#>   S3Path('s3://ladi/Images/FEMA_CAP/2020/70349/DSC_0003_02c30af6-911e-4e01-8c24-7644da2b8672.jpg')]

Created at 2021-05-21 20:38:47 PDT by reprexlite v0.4.2

hume
  • 2,413
  • 19
  • 21
0

A good option may also be to run aws cli command from lambda functions

import subprocess
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def run_command(command):
    command_list = command.split(' ')

    try:
        logger.info("Running shell command: \"{}\"".format(command))
        result = subprocess.run(command_list, stdout=subprocess.PIPE);
        logger.info("Command output:\n---\n{}\n---".format(result.stdout.decode('UTF-8')))
    except Exception as e:
        logger.error("Exception: {}".format(e))
        return False

    return True

def lambda_handler(event, context):
    run_command('/opt/aws s3 ls s3://bucket-name')
eugenhu
  • 1,168
  • 13
  • 22
0

I was stuck on this for an entire night because I just wanted to get the number of files under a subfolder but it was also returning one extra file in the content that was the subfolder itself,

After researching about it I found that this is how s3 works but I had a scenario where I unloaded the data from redshift in the following directory

s3://bucket_name/subfolder/<10 number of files>

and when I used

paginator.paginate(Bucket=price_signal_bucket_name,Prefix=new_files_folder_path+"/")

it would only return the 10 files, but when I created the folder on the s3 bucket itself then it would also return the subfolder

Conclusion

  1. If the whole folder is uploaded to s3 then listing the only returns the files under prefix
  2. But if the fodler was created on the s3 bucket itself then listing it using boto3 client will also return the subfolder and the files
Kartik
  • 240
  • 2
  • 13
0

First, create an s3 client object:

s3_client = boto3.client('s3')

Next, create a variable to hold the bucket name and folder. Pay attention to the slash "/" ending the folder name:

bucket_name = 'my-bucket'
folder = 'some-folder/'

Next, call s3_client.list_objects_v2 to get the folder's content object's metadata:

response = s3_client.list_objects_v2(
  Bucket=bucket_name,
  Prefix=folder
)

Finally, with the object's metadata, you can obtain the S3 object by calling the s3_client.get_object function:

for object_metadata in response['Contents']:
    object_key = object_metadata['Key']
    response = s3_client.get_object(
        Bucket=bucket_name,
        Key=object_key
    )
    object_body = response['Body'].read()
    print(object_body)

As you can see, the object content in the string format is available by calling response['Body'].read()

Anderson Marques
  • 808
  • 8
  • 13