programing

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

showcode 2023. 6. 9. 22:12
반응형

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

저는 AWS용 새로운 boto3 클라이언트로 "hello world"를 하려고 합니다.

제가 가지고 있는 사용 사례는 매우 간단합니다. S3에서 객체를 가져와 파일에 저장하는 것입니다.

boto 2.X에서는 다음과 같이 수행합니다.

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

boto 3. 동일한 작업을 수행할 수 있는 깨끗한 방법을 찾을 수 없어서 "Streaming" 개체를 수동으로 반복하고 있습니다.

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 기능이 있는지 궁금합니다.

최근에 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).

boto3는 이제 클라이언트보다 더 나은 인터페이스를 가지고 있습니다.

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

이것 자체로는 그것보다 엄청나게 좋지 않습니다.client승인된 답변(문서에 업로드 및 다운로드 재시도가 실패 시 더 잘 수행된다고 나와 있음에도 불구하고)에서 리소스가 일반적으로 더 인체공학적이라는 점(예: s3 버킷 및 개체 리소스가 클라이언트 방법보다 더 우수함)을 고려하면 이를 통해 리소스 계층을 드롭다운하지 않고도 유지할 수 있습니다.

Resources 일반적으로 클라이언트와 동일한 방법으로 생성할 수 있으며, 동일한 인수의 전부 또는 대부분을 사용하여 내부 클라이언트로 전달합니다.

시뮬레이션을 원하는 사용자를 위해set_contents_from_stringboto2 방법처럼, 당신은 시도할 수 있습니다.

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에서 두 문자열 모두IO 및 cStringIO가 사라졌습니다.사용StringIO다음과 같은 가져오기:

from io import StringIO

두 버전 모두 지원하기

try:
   from StringIO import StringIO
except ImportError:
   from io import StringIO
# 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"

참고: 인증을 별도로 구성한 것으로 가정합니다.아래 코드는 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')

파일 버전을 다운로드하려면 다음을 사용해야 합니다.get_object.

import boto3

bucket = 'bucketName'
prefix = 'path/to/file/'
filename = 'fileName.ext'

s3c = boto3.client('s3')
s3r = boto3.resource('s3')

if __name__ == '__main__':
    for version in s3r.Bucket(bucket).object_versions.filter(Prefix=prefix + filename):
        file = version.get()
        version_id = file.get('VersionId')
        obj = s3c.get_object(
            Bucket=bucket,
            Key=prefix + filename,
            VersionId=version_id,
        )
        with open(f"{filename}.{version_id}", 'wb') as f:
            for chunk in obj['Body'].iter_chunks(chunk_size=4096):
                f.write(chunk)

참조: https://botocore.amazonaws.com/v1/documentation/api/latest/reference/response.html

기본 구성과 다른 구성의 파일을 읽고 싶을 때는 직접 또는 복사 붙여넣기 코드를 사용하십시오.

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)

언급URL : https://stackoverflow.com/questions/29378763/how-to-save-s3-object-to-a-file-using-boto3

반응형