S3 nesnesini boto3 kullanarak bir dosyaya kaydetme


132

AWS için yeni boto3 istemcisi ile bir "merhaba dünya" yapmaya çalışıyorum .

Sahip olduğum kullanım durumu oldukça basit: nesneyi S3'ten alın ve dosyaya kaydedin.

Boto 2.XI'de şöyle yapardı:

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

Boto 3'te. Aynı şeyi yapmanın temiz bir yolunu bulamıyorum, bu yüzden "Akış" nesnesi üzerinde manuel olarak yineleme yapıyorum:

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)

veya

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)

Ve iyi çalışıyor. Merak ediyordum da aynı görevi yapacak "yerli" boto3 işlevi var mı?

Yanıtlar:


216

Son zamanlarda Boto3'te buna yardımcı olan (diğer şeylerin yanı sıra) bir özelleştirme var. Şu anda düşük seviyeli S3 istemcisinde gösterilmektedir ve şu şekilde kullanılabilir:

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())

Bu işlevler, büyük dosyalar için paralel olarak çok parçalı yüklemelerin yanı sıra dosya okuma / yazma işlemini otomatik olarak gerçekleştirecektir.

s3_client.download_fileBunun bir dizin oluşturmayacağını unutmayın . Olarak oluşturulabilir pathlib.Path('/path/to/file.txt').parent.mkdir(parents=True, exist_ok=True).


1
@Daniel: Cevabınız için teşekkürler. Boto3'te çok parçalı yüklemeyi kullanarak dosya yüklemek istersem cevabı yanıtlayabilir misiniz?
Rahul KP

1
@RahulKumarPatle upload_fileyöntemi, büyük dosyalar için otomatik olarak çok parçalı yüklemeleri kullanır.
Daniel

4
Bu yaklaşımı kullanarak kimlik bilgilerini nasıl iletirsiniz?
JHowIX

1
@JHowIX, kimlik bilgilerini global olarak yapılandırabilir (örn. Boto3.readthedocs.org/en/latest/guide/… ) veya istemciyi oluştururken bunları iletebilirsiniz. Mevcut seçenekler hakkında daha fazla bilgi için boto3.readthedocs.org/en/latest/reference/core/… sayfasına bakın !
Daniel

2
@VladNikiporoff "Kaynaktan hedefe yükle" "Kaynaktan hedefe indir"
jkdev

59

boto3 artık istemciden daha güzel bir arayüze sahip:

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

Bu, kendi başına clientkabul edilen cevaptan çok daha iyi değildir (ancak dokümanlar yükleme ve indirme başarısızlık durumunda daha iyi bir iş çıkardığını söylese de), ancak kaynakların genellikle daha ergonomik olduğu düşünüldüğünde (örneğin, s3 paketi ve nesne kaynakları) istemci yöntemlerinden daha iyidir) bu, düşmenize gerek kalmadan kaynak katmanında kalmanıza izin verir.

Resources genellikle müşterilerle aynı şekilde oluşturulabilir ve aynı argümanların tamamını veya çoğunu alır ve sadece iç müşterilerine iletirler.


1
Harika bir örnek ve orijinal soru bir nesneyi kaydetmeyi sorduğundan eklemek için, buradaki uygun yöntem my_bucket.upload_file()(veya my_bucket.upload_fileobj()bir BytesIO nesneniz varsa).
SMX

Dokümanlar bunun resourceyeniden denemede daha iyi bir iş çıkardığını tam olarak nerede söylüyor ? Böyle bir gösterge bulamadım.
Acumenus

42

set_contents_from_stringBenzeri boto2 yöntemlerini simüle etmek isteyenler için deneyebilirsiniz.

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())

Python3 için:

Python3'te hem StringIO hem de cStringIO gitti . StringIOİçe aktarımı şu şekilde kullanın :

from io import StringIO

Her iki sürümü desteklemek için:

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

15
Cevap bu. Soru şu: "Bir dizeyi boto3 kullanarak bir S3 nesnesine nasıl kaydedersiniz?"
jkdev

python3 için import io kullanmak zorundaydım; fake_handl e = io.StringIO (içindekiler)
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
AWS_ACCESS_KEY_ID'nizi veya AWS_SECRET_ACCESS_KEY'inizi asla kodunuza eklemeyin. Bunlar awscli aws configurekomutu ile tanımlanmalı ve tarafından otomatik olarak bulunacaktır botocore.
Miles Erickson

3

Varsayılandan farklı bir yapılandırmaya sahip bir dosyayı okumak istediğinizde, ya mpu.aws.s3_download(s3path, destination)doğrudan ya da kopyalayıp yapıştırılan kodu kullanmaktan çekinmeyin :

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)

Çalışmıyor. NameError: name '_s3_path_split' is not defined
Dave Liu

@DaveLiu İpucu için teşekkürler; Kodu değiştirdim. Yine de paket daha önce çalışmalıydı.
Martin Thoma

1

Not: Kimlik doğrulamayı ayrı olarak yapılandırdığınızı varsayıyorum. Aşağıdaki kod, tek nesneyi S3 klasöründen indirmektir.

import boto3

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

#Download object to the file    
s3.Bucket('mybucket').download_file('hello.txt', '/tmp/hello.txt')

Bu kod içeriden ve s3 klasöründen indirilmeyecek, bu şekilde yapmanın bir yolu var mı?
Marilu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.