boto3를 사용하여 S3 객체를 파일에 저장하는 방법


132

AWS의 새로운 boto3 클라이언트로 "hello world"를 수행하려고합니다 .

내가 가진 유스 케이스는 매우 간단합니다 .S3에서 객체를 가져 와서 파일에 저장하십시오.

boto 2.XI에서는 다음과 같이합니다.

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

boto에서 3. 동일한 작업을 수행하는 깔끔한 방법을 찾을 수 없으므로 "스트리밍"개체를 수동으로 반복하고 있습니다.

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)

또는

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)

그리고 잘 작동합니다. 동일한 작업을 수행하는 "기본"boto3 기능이 있는지 궁금합니다.

답변:


216

Boto3에 최근 들어온 커스터마이제이션이 있습니다. 현재 저수준 S3 클라이언트에 노출되어 있으며 다음과 같이 사용할 수 있습니다.

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

이 기능은 파일 읽기 / 쓰기뿐만 아니라 대용량 파일에 대해 멀티 파트 업로드를 자동으로 처리합니다.

s3_client.download_file디렉토리를 생성하지 않습니다. 로 만들 수 있습니다 pathlib.Path('/path/to/file.txt').parent.mkdir(parents=True, exist_ok=True).


1
@Daniel : 답장을 보내 주셔서 감사합니다. boto3에서 멀티 파트 업로드를 사용하여 파일을 업로드하려는 경우 답변에 응답 할 수 있습니까?
Rahul KP

1
@RahulKumarPatle이 upload_file메소드는 큰 파일에 대해 멀티 파트 업로드를 자동으로 사용합니다.
다니엘

4
이 방법을 사용하여 자격 증명을 어떻게 전달합니까?
JHowIX

1
@JHowIX 자격 증명을 전체적으로 구성하거나 (예 : boto3.readthedocs.org/en/latest/guide/… 참조 ) 클라이언트를 만들 때 전달할 수 있습니다. 사용 가능한 옵션에 대한 자세한 내용은 boto3.readthedocs.org/en/latest/reference/core/… 를 참조하십시오 !
Daniel

2
@VladNikiporoff "소스에서 목적지로 업로드" "소스에서 목적지로 다운로드"
jkdev

59

boto3는 이제 클라이언트보다 더 멋진 인터페이스를 갖습니다.

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

문서 자체가 client허용 된 답변 보다 훨씬 우수 하지는 않지만 (문서는 실패시 업로드 및 다운로드를 다시 시도하는 것이 더 나은 작업을 수행한다고 말하지만) 일반적으로 자원 이보다 인체 공학적이라고 생각합니다 (예 : s3 버킷객체 자원) 클라이언트 메소드보다 우수합니다.) 이렇게하면 드롭 다운 할 필요없이 리소스 계층에 머무를 수 있습니다.

Resources 일반적으로 클라이언트와 같은 방식으로 생성 될 수 있으며, 동일한 인수를 전부 또는 대부분 인수하여 내부 클라이언트에게 전달합니다.


1
좋은 예를 들어, 원래 질문에 객체 저장에 대해 질문하기 때문에 여기에 관련 방법이 있습니다 my_bucket.upload_file()(또는 my_bucket.upload_fileobj()BytesIO 객체가있는 경우).
SMX

resource다시 시도 할 때 더 나은 작업을 수행한다는 문서는 정확히 어디에 있습니까? 그런 징후를 찾지 못했습니다.
Acumenus

42

set_contents_from_stringlike boto2 메소드 를 시뮬레이트 하려는 사용자는 시도해 볼 수 있습니다.

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의 경우 :

python3에서는 StringIO와 cStringIO가 모두 사라졌습니다 . 다음 StringIO과 같이 가져 오기를 사용하십시오 .

from io import StringIO

두 버전을 모두 지원하려면

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

15
그게 답입니다. 다음은 질문입니다. "boto3를 사용하여 문자열을 S3 객체에 어떻게 저장합니까?"
jkdev

python3의 경우 import io를 사용해야했습니다. fake_handl e = io.StringIO (contents)
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 또는 AWS_SECRET_ACCESS_KEY를 코드에 넣지 마십시오. 이들은 awscli aws configure명령 으로 정의해야하며에 의해 자동으로 발견됩니다 botocore.
Miles Erickson

3

기본 구성과 다른 구성으로 파일을 읽으려면 mpu.aws.s3_download(s3path, destination)직접 또는 복사하여 붙여 넣은 코드를 사용하십시오.

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)

작동하지 않습니다. NameError: name '_s3_path_split' is not defined
데이브 리우

@DaveLiu 힌트를 주셔서 감사합니다; 코드를 조정했습니다. 그러나 패키지는 이전에 작동 했어야합니다.
마틴 토마

1

참고 : 인증을 별도로 구성했다고 가정합니다. 아래 코드는 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')
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.