Próbuję stworzyć "hello world" z nowym klientem boto3 dla AWS.
Mój przypadek użycia jest dość prosty: pobierz obiekt z S3 i zapisz go do pliku.
W boto 2.X zrobiłbym to tak:
import boto
key = boto.connect_s3().get_bucket('foo').get_key('foo')
key.get_contents_to_filename('/tmp/foo')
Na boto 3. Nie mogę znaleźć prostego sposobu na zrobienie tego samego, więc ręcznie iteruję po obiekcie „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)
lub
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)
I działa dobrze. Zastanawiałem się, czy jest jakaś "natywna" funkcja boto3, która będzie wykonywać to samo zadanie?
upload_file
metoda automatycznie użyje przesyłania wieloczęściowego w przypadku dużych plików.boto3 ma teraz ładniejszy interfejs niż klient:
resource = boto3.resource('s3') my_bucket = resource.Bucket('MyBucket') my_bucket.download_file(key, local_filename)
To samo w sobie nie jest ogromnie lepszy niż
client
odpowiedź dozwolone (chociaż docs powiedzieć, że ma lepszą pracę Ponowna próba przesyłanie i pobieranie na niepowodzenie), ale biorąc pod uwagę, że zasoby są na ogół bardziej ergonomiczne (np S3 wiadra i obiektów zasoby są ładniejsze niż metody klienckie) pozwala to pozostać w warstwie zasobów bez konieczności opuszczania.Resources
generalnie mogą być tworzone w taki sam sposób jak klienci, przyjmują wszystkie lub większość tych samych argumentów i po prostu przekazują je swoim klientom wewnętrznym.źródło
my_bucket.upload_file()
(lubmy_bucket.upload_fileobj()
jeśli masz obiekt BytesIO).resource
lepiej radzi sobie z ponowną próbą? Nie mogłem znaleźć takiego wskazania.Dla tych z Was, którzy chcieliby symulować metody
set_contents_from_string
podobne do boto2, możesz spróbować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())
W przypadku Python3:
W pythonie3 zniknęły zarówno StringIO, jak i cStringIO . Użyj
StringIO
importu jak:from io import StringIO
Aby obsługiwać obie wersje:
try: from StringIO import StringIO except ImportError: from io import StringIO
źródło
# 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"
źródło
aws configure
polecenia awscli, a zostaną znalezione automatycznie przezbotocore
.Uwaga: zakładam, że skonfigurowałeś uwierzytelnianie oddzielnie. Poniższy kod służy do pobrania pojedynczego obiektu z wiadra 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')
źródło
Jeśli chcesz odczytać plik z inną konfiguracją niż domyślna, możesz użyć
mpu.aws.s3_download(s3path, destination)
bezpośrednio lub kodu wklejonego: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)
źródło
NameError: name '_s3_path_split' is not defined
Jeśli chcesz pobrać wersję pliku, musisz użyć
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)
Odniesienie: https://botocore.amazonaws.com/v1/documentation/api/latest/reference/response.html
źródło