Cách lưu đối tượng S3 vào một tệp bằng boto3


131

Tôi đang cố gắng thực hiện "thế giới xin chào" với ứng dụng khách boto3 mới cho AWS.

Ca sử dụng tôi có khá đơn giản: lấy đối tượng từ S3 và lưu nó vào tệp.

Trong boto 2.XI sẽ làm như thế này:

import boto
key = boto.connect_s3().get_bucket('foo').get_key('foo')
key.get_contents_to_filename('/tmp/foo')

Trong boto 3. Tôi không thể tìm thấy một cách rõ ràng để làm điều tương tự, vì vậy tôi đang lặp lại thủ công đối tượng "Truyền phát":

import boto3
key = boto3.resource('s3').Object('fooo', 'docker/my-image.tar.gz').get()
with open('/tmp/my-image.tar.gz', 'w') as f:
    chunk = key['Body'].read(1024*8)
    while chunk:
        f.write(chunk)
        chunk = key['Body'].read(1024*8)

hoặc là

import boto3
key = boto3.resource('s3').Object('fooo', 'docker/my-image.tar.gz').get()
with open('/tmp/my-image.tar.gz', 'w') as f:
    for chunk in iter(lambda: key['Body'].read(4096), b''):
        f.write(chunk)

Và nó hoạt động tốt. Tôi đã tự hỏi là có bất kỳ chức năng boto3 "bản địa" sẽ làm nhiệm vụ tương tự?

Câu trả lời:


215

Có một tùy chỉnh đã đi vào Boto3 gần đây giúp ích cho việc này (trong số những thứ khác). Nó hiện đang được hiển thị trên máy khách S3 cấp thấp và có thể được sử dụng như thế này:

s3_client = boto3.client('s3')
open('hello.txt').write('Hello, world!')

# Upload the file to S3
s3_client.upload_file('hello.txt', 'MyBucket', 'hello-remote.txt')

# Download the file from S3
s3_client.download_file('MyBucket', 'hello-remote.txt', 'hello2.txt')
print(open('hello2.txt').read())

Các chức năng này sẽ tự động xử lý việc đọc / ghi tệp cũng như thực hiện tải lên nhiều phần song song cho các tệp lớn.

Lưu ý rằng s3_client.download_filesẽ không tạo một thư mục. Nó có thể được tạo ra như pathlib.Path('/path/to/file.txt').parent.mkdir(parents=True, exist_ok=True).


1
@Daniel: Cảm ơn bạn đã trả lời. Bạn có thể trả lời câu trả lời nếu tôi muốn tải lên tập tin bằng cách tải lên nhiều phần trong boto3.
Rahul KP

1
@RahulKumarPatle upload_filephương thức sẽ tự động sử dụng tải lên nhiều phần cho các tệp lớn.
Daniel

4
Làm thế nào để bạn vượt qua bạn thông tin bằng cách sử dụng phương pháp này?
JHowIX

1
@JHowIX bạn có thể định cấu hình thông tin đăng nhập trên toàn cầu (ví dụ: xem boto3.readthedocs.org/en/latest/guide/ trộm ) hoặc bạn có thể vượt qua chúng khi tạo ứng dụng khách. Xem boto3.readthedocs.org/en/latest/reference/core/ khuyên để biết thêm thông tin về các tùy chọn có sẵn!
Daniel

2
@VladNikiporoff "Tải lên từ nguồn đến đích" "Tải xuống từ nguồn đến đích"
jkdev

59

boto3 hiện có giao diện đẹp hơn máy khách:

resource = boto3.resource('s3')
my_bucket = resource.Bucket('MyBucket')
my_bucket.download_file(key, local_filename)

Điều này tự nó không tốt hơn nhiều so với clientcâu trả lời được chấp nhận (mặc dù các tài liệu nói rằng nó thực hiện công việc tốt hơn khi thử lại tải lên và tải xuống khi thất bại) nhưng xem xét rằng tài nguyên nói chung là tiện dụng hơn (ví dụ, s3 và tài nguyên đối tượng đẹp hơn các phương thức máy khách) điều này cho phép bạn ở lại lớp tài nguyên mà không phải thả xuống.

Resources nói chung có thể được tạo theo cùng một cách với khách hàng và họ lấy tất cả hoặc hầu hết các đối số tương tự và chỉ chuyển tiếp chúng đến các máy khách nội bộ của họ.


1
Ví dụ tuyệt vời và để thêm vào vì câu hỏi ban đầu hỏi về việc lưu một đối tượng, phương thức liên quan ở đây là my_bucket.upload_file()(hoặc my_bucket.upload_fileobj()nếu bạn có một đối tượng BytesIO).
SMX

Chính xác thì các tài liệu nói rằng resourcelàm một công việc tốt hơn khi thử lại? Tôi không thể tìm thấy bất kỳ dấu hiệu như vậy.
Acumenus

42

Đối với những người muốn mô phỏng các set_contents_from_stringphương thức boto2, bạn có thể thử

import boto3
from cStringIO import StringIO

s3c = boto3.client('s3')
contents = 'My string to save to S3 object'
target_bucket = 'hello-world.by.vor'
target_file = 'data/hello.txt'
fake_handle = StringIO(contents)

# notice if you do fake_handle.read() it reads like a file handle
s3c.put_object(Bucket=target_bucket, Key=target_file, Body=fake_handle.read())

Đối với Python3:

Trong python3, cả StringIO và cStringIO đều biến mất . Sử dụng StringIOnhập như:

from io import StringIO

Để hỗ trợ cả hai phiên bản:

try:
   from StringIO import StringIO
except ImportError:
   from io import StringIO

15
Đó là câu trả lời. Đây là câu hỏi: "Làm thế nào để bạn lưu một chuỗi vào một đối tượng S3 bằng boto3?"
jkdev

Đối với python3 tôi đã phải sử dụng nhập io; fake_handl e = io.StringIO (nội dung)
Felix

16
# Preface: File is json with contents: {'name': 'Android', 'status': 'ERROR'}

import boto3
import io

s3 = boto3.resource('s3')

obj = s3.Object('my-bucket', 'key-to-file.json')
data = io.BytesIO()
obj.download_fileobj(data)

# object is now a bytes string, Converting it to a dict:
new_dict = json.loads(data.getvalue().decode("utf-8"))

print(new_dict['status']) 
# Should print "Error"

14
Không bao giờ đặt AWS_ACCESS_KEY_ID hoặc AWS_SECRET_ACCESS_KEY của bạn vào mã của bạn. Chúng nên được định nghĩa bằng aws configurelệnh awscli và chúng sẽ được tìm thấy tự động bởi botocore.
Miles Erickson

3

Khi bạn muốn đọc một tệp có cấu hình khác với cấu hình mặc định, vui lòng sử dụng mpu.aws.s3_download(s3path, destination)trực tiếp hoặc mã được sao chép:

def s3_download(source, destination,
                exists_strategy='raise',
                profile_name=None):
    """
    Copy a file from an S3 source to a local destination.

    Parameters
    ----------
    source : str
        Path starting with s3://, e.g. 's3://bucket-name/key/foo.bar'
    destination : str
    exists_strategy : {'raise', 'replace', 'abort'}
        What is done when the destination already exists?
    profile_name : str, optional
        AWS profile

    Raises
    ------
    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
    """
    exists_strategies = ['raise', 'replace', 'abort']
    if exists_strategy not in exists_strategies:
        raise ValueError('exists_strategy \'{}\' is not in {}'
                         .format(exists_strategy, exists_strategies))
    session = boto3.Session(profile_name=profile_name)
    s3 = session.resource('s3')
    bucket_name, key = _s3_path_split(source)
    if os.path.isfile(destination):
        if exists_strategy is 'raise':
            raise RuntimeError('File \'{}\' already exists.'
                               .format(destination))
        elif exists_strategy is 'abort':
            return
    s3.Bucket(bucket_name).download_file(key, destination)

from collections import namedtuple

S3Path = namedtuple("S3Path", ["bucket_name", "key"])


def _s3_path_split(s3_path):
    """
    Split an S3 path into bucket and key.

    Parameters
    ----------
    s3_path : str

    Returns
    -------
    splitted : (str, str)
        (bucket, key)

    Examples
    --------
    >>> _s3_path_split('s3://my-bucket/foo/bar.jpg')
    S3Path(bucket_name='my-bucket', key='foo/bar.jpg')
    """
    if not s3_path.startswith("s3://"):
        raise ValueError(
            "s3_path is expected to start with 's3://', " "but was {}"
            .format(s3_path)
        )
    bucket_key = s3_path[len("s3://"):]
    bucket_name, key = bucket_key.split("/", 1)
    return S3Path(bucket_name, key)

Không hoạt động. NameError: name '_s3_path_split' is not defined
Dave Liu

@DaveLiu Cảm ơn bạn đã gợi ý; Tôi đã điều chỉnh mã. Các gói nên đã làm việc trước đây, mặc dù.
Martin Thoma

1

Lưu ý: Tôi giả sử bạn đã cấu hình xác thực riêng biệt. Dưới đây mã là để tải xuống đối tượng duy nhất từ ​​nhóm S3.

import boto3

#initiate s3 client 
s3 = boto3.resource('s3')

#Download object to the file    
s3.Bucket('mybucket').download_file('hello.txt', '/tmp/hello.txt')
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.