使用boto3从S3存储桶中读取文件内容

123

我通过执行以下操作读取S3存储桶中的文件名:

objs = boto3.client.list_objects(Bucket='my_bucket')
    while 'Contents' in objs.keys():
        objs_contents = objs['Contents']
        for i in range(len(objs_contents)):
            filename = objs_contents[i]['Key']

现在,我需要获取文件的实际内容,类似于open(filename).readlines()的方式。最好的方法是什么?

10个回答

149
提供了一种资源模型,使得像迭代对象之类的任务更加容易。不幸的是,StreamingBody 不支持 readlinereadlines
s3 = boto3.resource('s3')
bucket = s3.Bucket('test-bucket')
# Iterates through all the objects, doing the pagination for you. Each obj
# is an ObjectSummary, so it doesn't contain the body. You'll need to call
# get to get the whole body.
for obj in bucket.objects.all():
    key = obj.key
    body = obj.get()['Body'].read()

2
我需要手动在脚本中配置客户端,因此我通过客户端进行了传递,例如:client = boto3.client( 's3', aws_access_key_id="", aws_secret_access_key="*" )。有没有一种方法可以在不使用客户端的情况下向资源提供访问密钥? - mar tin
3
你可以以相同的方式配置资源。 - Jordon Phillips
4
如果S3中有文件夹,我该如何读取文件呢?例如我的桶名为A,A有个文件夹B,B有个文件夹C,C里面有个Readme.csv文件。请问如何读取这个文件?如果我们只是在桶直接有文件的情况下,你的解决方案很好用,但如果存在多层文件夹,应该怎么做呢?谢谢。 - Kshitij Marwah
2
我们可以获取正文,但如何逐行读取正文内容呢? - Gabriel Wu
11
是的,它正在发出请求,但您并没有下载对象,只是列出它们。您可以使用.filter()减少列表请求。或者如果您知道所需的键,可以直接使用bucket.Object('mykey')获取它。 - Jordon Phillips
显示剩余8条评论

52

使用客户端而不是资源:

s3 = boto3.client('s3')
bucket='bucket_name'
result = s3.list_objects(Bucket = bucket, Prefix='/something/')
for o in result.get('Contents'):
    data = s3.get_object(Bucket=bucket, Key=o.get('Key'))
    contents = data['Body'].read()
    print(contents.decode("utf-8"))

添加 print(body) 以显示内容。 - Refael

41

您可以考虑使用支持迭代器的smart_open模块:

from smart_open import smart_open

# stream lines from an S3 object
for line in smart_open('s3://mybucket/mykey.txt', 'rb'):
    print(line.decode('utf8'))

以及上下文管理器:

with smart_open('s3://mybucket/mykey.txt', 'rb') as s3_source:
    for line in s3_source:
         print(line.decode('utf8'))

    s3_source.seek(0)  # seek to the beginning
    b1000 = s3_source.read(1000)  # read 1000 bytes

可以在https://pypi.org/project/smart_open/找到smart_open


1
文档声称“S3读取器可以透明地支持gzip压缩的内容”,但我个人还没有亲自尝试过。 - caffreyd
1
我尝试了一下,使用gzip压缩的内容完美地运行了! - IY4

24

当您想要使用不同于默认配置的配置读取文件时,可以直接使用mpu.aws.s3_read(s3path),或者复制粘贴以下代码:

def s3_read(source, profile_name=None):
    """
    Read a file from an S3 source.

    Parameters
    ----------
    source : str
        Path starting with s3://, e.g. 's3://bucket-name/key/foo.bar'
    profile_name : str, optional
        AWS profile

    Returns
    -------
    content : bytes

    botocore.exceptions.NoCredentialsError
        Botocore is not able to find your credentials. Either specify
        profile_name or add the environment variables AWS_ACCESS_KEY_ID,
        AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN.
        See https://boto3.readthedocs.io/en/latest/guide/configuration.html
    """
    session = boto3.Session(profile_name=profile_name)
    s3 = session.client('s3')
    bucket_name, key = mpu.aws._s3_path_split(source)
    s3_object = s3.get_object(Bucket=bucket_name, Key=key)
    body = s3_object['Body']
    return body.read()

18

如果您已经知道filename,可以使用boto3内置的download_fileobj方法进行下载。

import boto3

from io import BytesIO

session = boto3.Session()
s3_client = session.client("s3")

f = BytesIO()
s3_client.download_fileobj("bucket_name", "filename", f)
print(f.getvalue())

2
使用BytesIO(或StringIO)对象时,f.seek(0)是不必要的。read从当前位置开始读取,但getvalue始终从位置0开始读取。 - Adam Hoelscher
很好的观点@adam。有可能有人实际上需要read来满足他们的用例。我只是为了演示目的使用了getvalue - reubano

4
import boto3

print("started")

s3 = boto3.resource('s3',region_name='region_name', aws_access_key_id='your_access_id', aws_secret_access_key='your access key')

obj = s3.Object('bucket_name','file_name')

data=obj.get()['Body'].read()

print(data)

2
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community

1

以下是使用boto3从s3存储桶访问文件内容的正确且经过测试的代码。截至发布日期,该代码对我有效。

def get_file_contents(bucket, prefix):
    s3 = boto3.resource('s3')
    s3.meta.client.meta.events.register('choose-signer.s3.*', disable_signing)
    bucket = s3.Bucket(bucket)
    for obj in bucket.objects.filter(Prefix=prefix):
        key = obj.key
        body = obj.get()['Body'].read()
        print(body)
        return body

get_file_contents('coderbytechallengesandbox', '__cb__')

0
在这种情况下,与boto3的另一种选择是s3fs
from s3fs import S3FileSystem
s3 = S3FileSystem()
bucket = 's3://your-bucket'

def read_file(key):
    with s3.open(f'{s3_path}/{key}', 'r') as file:  # s3://bucket/file.txt
        return file.readlines()

for obj in bucket.objects.all():
    key = obj.key
    lines = read_file(key)
    ...

0

对我来说,最好的方法是这样的:

result = s3.list_objects(Bucket = s3_bucket, Prefix=s3_key)
for file in result.get('Contents'):
    data = s3.get_object(Bucket=s3_bucket, Key=file.get('Key'))
    contents = data['Body'].read()
    #if Float types are not supported with dynamodb; use Decimal types instead
    j = json.loads(contents, parse_float=Decimal)
    for item in j:
       timestamp = item['timestamp']

       table.put_item(
           Item={
            'timestamp': timestamp
           }
      )

一旦您有了内容,您可以通过另一个循环运行它,以便将其写入DynamoDB表中。


0
请注意,Boto3现在已停止更新Resources,建议的方法是返回使用Client
因此,我相信@Climbs_lika_Spyder的回答现在应该成为被接受的答案。
参考:https://boto3.amazonaws.com/v1/documentation/api/latest/guide/resources.html 引用: 警告:AWS Python SDK团队不再计划支持boto3中的资源界面。不再考虑涉及资源模型的新更改请求,并且资源界面将不受下一版Python AWS SDK的支持。 AWS SDK团队正在努力在SDK之间实现更一致的功能,并且在各个SDK中实现自定义抽象不是可持续的解决方案。未来的特性请求将需要在跨SDK级别上考虑。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接