Як записати файл або дані на об'єкт S3 за допомогою boto3


105

У boto 2 ви можете записати на об'єкт S3 за допомогою цих методів:

Чи є еквівалент boto 3? Що таке метод boto3 для збереження даних до об'єкта, що зберігається на S3?

Відповіді:


212

У boto 3 методи 'Key.set_contents_from_' були замінені на

Наприклад:

import boto3

some_binary_data = b'Here we have some data'
more_binary_data = b'Here we have some more data'

# Method 1: Object.put()
s3 = boto3.resource('s3')
object = s3.Object('my_bucket_name', 'my/key/including/filename.txt')
object.put(Body=some_binary_data)

# Method 2: Client.put_object()
client = boto3.client('s3')
client.put_object(Body=more_binary_data, Bucket='my_bucket_name', Key='my/key/including/anotherfilename.txt')

Крім того, бінарні дані можуть надходити з читання файлу, як описано в офіційних документах, порівнюючи boto 2 і boto 3 :

Зберігання даних

Збереження даних з файлу, потоку або рядка легко:

# Boto 2.x
from boto.s3.key import Key
key = Key('hello.txt')
key.set_contents_from_file('/tmp/hello.txt')

# Boto 3
s3.Object('mybucket', 'hello.txt').put(Body=open('/tmp/hello.txt', 'rb'))

botocore.exceptions.NoCredentialsError: Не вдається знайти облікові дані, як це виправити?
деепак мертвий

2
@deepakmurthy Я не впевнений, чому ви отримуєте цю помилку ... Вам потрібно буде задати нове запитання щодо переповнення стека та надати більш детальну інформацію про проблему.
jkdev

1
Коли я намагаюся, s3.Object().put()я опиняюсь об'єктом з нулем content-length. Для мене put()приймає лише рядкові дані, але, put(str(binarydata)) схоже, є якісь проблеми з кодуванням. Я закінчую об'єктом приблизно в 3 рази розмір оригінальних даних, що робить його марним для мене.
користувач1129682

@ user1129682 Я не впевнений, чому це так. Не могли б ви задати нове запитання та надати більше деталей?
jkdev

@jkdev Було б чудово, якби ви могли поглянути .
користувач1129682

48

boto3 також має метод для завантаження файлу безпосередньо:

s3.Bucket('bucketname').upload_file('/local/file/here.txt','folder/sub/path/to/s3key')

http://boto3.readthedocs.io/en/latest/reference/services/s3.html#S3.Bucket.upload_file


5
Це добре, але воно не дозволяє зберігати дані, які наразі є в пам'яті.
Рейд

3
@Reid: для файлів у пам'яті ви можете використовувати s3.Bucket(...).upload_fileobj()метод замість цього.
свохара

37

Вам більше не доведеться конвертувати вміст у бінарний файл перед тим, як записувати у файл у S3. У наступному прикладі створюється новий текстовий файл (званий newfile.txt) у відрі S3 із вмістом рядка:

import boto3

s3 = boto3.resource(
    's3',
    region_name='us-east-1',
    aws_access_key_id=KEY_ID,
    aws_secret_access_key=ACCESS_KEY
)
content="String content to write to a new S3 file"
s3.Object('my-bucket-name', 'newfile.txt').put(Body=content)

Поняття не маю, що до моєї дії "доступу" немає доступу. Я створив це відро і поставив свій канонічний ідентифікатор під список доступу.
Чен Лін

Як ви даєте prefixв цьому випадку? Значить, що робити, якщо ви хочете зберегти файл у my-bucket-name/subfolder/?
kev

3
@kev ви можете вказати, що поряд з назвою "підпапка / newfile.txt" замість "newfile.txt"
Carrillo

Re "Вам більше не доведеться конвертувати вміст у бінарний файл перед тим, як записувати у файл у S3.", Це десь задокументовано? Я дивився на boto3.amazonaws.com/v1/documentation/api/latest/reference/… , і думав, що він приймає лише байти. Я не впевнений, що саме є об'єктом, схожим на файл, але не думаю, що він включає рядки.
Емма

Можливо, я порівнюю це з download_fileobj (), який призначений для великих завантажень файлів у багато частинах. Методи завантаження вимагають пошуку файлових об’єктів , але put () дозволяє записувати рядки безпосередньо у файл у відрі, що зручно для функцій лямбда для динамічного створення та запису файлів у відро S3.
Franke

28

Ось хороший трюк, щоб прочитати JSON з s3:

import json, boto3
s3 = boto3.resource("s3").Bucket("bucket")
json.load_s3 = lambda f: json.load(s3.Object(key=f).get()["Body"])
json.dump_s3 = lambda obj, f: s3.Object(key=f).put(Body=json.dumps(obj))

Тепер ви можете використовувати json.load_s3та json.dump_s3з тим же API, що loadіdump

data = {"test":0}
json.dump_s3(data, "key") # saves json to s3://bucket/key
data = json.load_s3("key") # read json from s3://bucket/key

2
Відмінно. Для того, щоб отримати його на роботу, я додав цей додатковий біт: ...["Body"].read().decode('utf-8').
sedeh

Чудова ідея. У будь-якому випадку, це дає певний простір для вдосконалення імен.
Ян Вльчинський

Пропонуємо переписати цю приємну ідею: gist.github.com/vlcinsky/bbeda4321208aa98745afc29b58e90ac
Ян Вльчинський

14

Більш чітка та коротка версія, яку я використовую для завантаження файлів на леті до заданого відра S3 та підпапки,

import boto3

BUCKET_NAME = 'sample_bucket_name'
PREFIX = 'sub-folder/'

s3 = boto3.resource('s3')

# Creating an empty file called "_DONE" and putting it in the S3 bucket
s3.Object(BUCKET_NAME, PREFIX + '_DONE').put(Body="")

Примітка . ВИНАГА слід покласти свої облікові дані AWS ( aws_access_key_idі aws_secret_access_key) в окремий файл, наприклад,~/.aws/credentials


Яке еквівалентне місце розташування Windows для файлу облікових даних AWS, оскільки Windows не підтримуватиме~
Хамман Самуель

1
@HammanSamuel ви можете зберігати його якC:\Users\username\.aws\credentials
kev

1

Варто згадати smart-open, який використовується boto3як бек-енд.

smart-openє заміною для пітона , openякі можуть відкривати файли s3, а також ftp, httpі багатьох інших протоколів.

наприклад

from smart_open import open
import json
with open("s3://your_bucket/your_key.json", 'r') as f:
    data = json.load(f)

Вхідні дані aws завантажуються за допомогою облікових даних boto3 , як правило, файлів у ~/.aws/dir або змінної середовища.


1
Хоча цей відповідь є інформативним, він не дотримується відповіді на початкове запитання - що таке еквіваленти boto3 певних методів бото.
robinhood91

1
Smart open використовує boto3
Урі Горен

1

Ви можете скористатися наведеним нижче кодом для запису, наприклад, зображення на S3 у 2019 році. Щоб мати змогу підключитися до S3, вам доведеться встановити AWS CLI за допомогою команди pip install awscli, а потім ввести кілька облікових даних за допомогою команди aws configure:

import urllib3
import uuid
from pathlib import Path
from io import BytesIO
from errors import custom_exceptions as cex

BUCKET_NAME = "xxx.yyy.zzz"
POSTERS_BASE_PATH = "assets/wallcontent"
CLOUDFRONT_BASE_URL = "https://xxx.cloudfront.net/"


class S3(object):
    def __init__(self):
        self.client = boto3.client('s3')
        self.bucket_name = BUCKET_NAME
        self.posters_base_path = POSTERS_BASE_PATH

    def __download_image(self, url):
        manager = urllib3.PoolManager()
        try:
            res = manager.request('GET', url)
        except Exception:
            print("Could not download the image from URL: ", url)
            raise cex.ImageDownloadFailed
        return BytesIO(res.data)  # any file-like object that implements read()

    def upload_image(self, url):
        try:
            image_file = self.__download_image(url)
        except cex.ImageDownloadFailed:
            raise cex.ImageUploadFailed

        extension = Path(url).suffix
        id = uuid.uuid1().hex + extension
        final_path = self.posters_base_path + "/" + id
        try:
            self.client.upload_fileobj(image_file,
                                       self.bucket_name,
                                       final_path
                                       )
        except Exception:
            print("Image Upload Error for URL: ", url)
            raise cex.ImageUploadFailed

        return CLOUDFRONT_BASE_URL + id
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.