Как сохранить объект S3 в файл с помощью boto3
Я пытаюсь сделать "привет мир" с новым клиентом boto3 для AWS.
Практический случай, который у меня есть, довольно прост: получить объект из 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, которая будет выполнять ту же задачу?
Ответы
Ответ 1
В 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)
.
Ответ 2
boto3 теперь имеет более удобный интерфейс, чем клиент:
resource = boto3.resource('s3')
my_bucket = resource.Bucket('MyBucket')
my_bucket.download_file(key, local_filename)
Это само по себе не намного лучше, чем client
в принятом ответе (хотя документы говорят, что он лучше справляется с повторной загрузкой и загрузкой при сбое), но учитывая, что ресурсы, как правило, более эргономичны (например, ресурсы s3 bucket и object лучше, чем клиентские методы), это позволяет вам оставайтесь на уровне ресурсов, не отпадая.
Resources
обычно могут создаваться так же, как клиенты, и они принимают все или большинство тех же аргументов и просто перенаправляют их своим внутренним клиентам.
Ответ 3
Для тех из вас, кто хотел бы имитировать set_contents_from_string
методы 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
Ответ 4
# 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"
Ответ 5
Если вы хотите прочитать файл с конфигурацией, отличной от конфигурации по умолчанию, вы можете использовать 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)
Ответ 6
Примечание. Я предполагаю, что вы настроили аутентификацию отдельно. Ниже приведен код для загрузки одного объекта из корзины 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')