Jak zapisać plik lub dane do obiektu S3 przy użyciu boto3

Odpowiedzi:

219

W boto 3 metody „Key.set_contents_from_” zostały zastąpione przez

Na przykład:

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

Alternatywnie, dane binarne mogą pochodzić z odczytu pliku, jak opisano w oficjalnych dokumentach porównujących boto 2 i boto 3 :

Przechowywanie danych

Przechowywanie danych z pliku, strumienia lub ciągu znaków jest łatwe:

# 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'))
jkdev
źródło
botocore.exceptions.NoCredentialsError: Nie można zlokalizować poświadczeń, jak to naprawić?
deepak murthy
2
@deepakmurthy Nie jestem pewien, dlaczego otrzymujesz ten błąd ... Musisz zadać nowe pytanie dotyczące przepełnienia stosu i podać więcej szczegółów na temat problemu.
jkdev
1
Kiedy próbuję s3.Object().put(), otrzymuję obiekt z zerem content-length. U mnie put()akceptuje tylko dane ciągów, ale put(str(binarydata)) wydaje się, że ma jakieś problemy z kodowaniem. W rezultacie otrzymuję obiekt mniej więcej 3 razy większy od oryginalnych danych, co czyni go bezużytecznym dla mnie.
user1129682
@ user1129682 Nie jestem pewien, dlaczego tak jest. Czy mógłbyś zadać nowe pytanie i podać więcej szczegółów?
jkdev
@jkdev Byłoby wspaniale, gdybyś mógł rzucić okiem .
user1129682
51

boto3 ma również metodę bezpośredniego przesyłania pliku:

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

EM Bee
źródło
5
To dobrze, ale nie pozwala na przechowywanie danych znajdujących się obecnie w pamięci.
Reid
3
@Reid: w przypadku plików w pamięci możesz s3.Bucket(...).upload_fileobj()zamiast tego użyć metody.
svohara
40

Nie musisz już konwertować zawartości do formatu binarnego przed zapisaniem do pliku w S3. Poniższy przykład tworzy nowy plik tekstowy (o nazwie newfile.txt) w zasobniku S3 z zawartością ciągu:

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)
Franke
źródło
Nie mam pojęcia, że ​​moja akcja „put” nie ma dostępu. Utworzyłem ten zasobnik i umieściłem mój identyfikator kanoniczny pod listą dostępu.
Chen Lin
Jak podać prefixw tym przypadku? To znaczy, co jeśli chcesz przechowywać plik w my-bucket-name/subfolder/?
kev
3
@kev możesz określić, że wraz z nazwą pliku „podfolder / nowy plik.txt” zamiast „nowy plik.txt”
Madhava Carrillo
Re: „Nie musisz już konwertować zawartości do formatu binarnego przed zapisaniem do pliku w S3.”, Czy jest to gdzieś udokumentowane? Patrzyłem na boto3.amazonaws.com/v1/documentation/api/latest/reference/… i pomyślałem, że akceptuje tylko bajty. Nie jestem pewien, co dokładnie stanowi „możliwy do przeszukania obiekt podobny do pliku”, ale nie sądziłem, że obejmuje to łańcuchy.
Emma
Mogłem porównać to z download_fileobj (), który jest przeznaczony do przesyłania dużych plików wieloczęściowych. Metody przesyłania wymagają obiektów plików, które można przeszukiwać , ale metoda put () umożliwia zapisywanie ciągów znaków bezpośrednio do pliku w zasobniku, co jest przydatne dla funkcji lambda do dynamicznego tworzenia i zapisywania plików w zasobniku S3.
Franke
28

Oto fajna sztuczka do czytania JSON z 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))

Teraz możesz używać json.load_s3i json.dump_s3z tym samym interfejsem API, co loadidump

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
Uri Goren
źródło
2
Doskonały. Aby dostać się do pracy, dodałem ten dodatkowy bit: ...["Body"].read().decode('utf-8').
sedeh
Świetny pomysł. W każdym razie zapewnia miejsce na ulepszenia nazewnictwa.
Jan Vlcinsky
Proponowane przepisanie tego fajnego pomysłu: gist.github.com/vlcinsky/bbeda4321208aa98745afc29b58e90ac
Jan Vlcinsky
15

Czystsza i zwięzła wersja, której używam do przesyłania plików w locie do danego wiadra S3 i podfolderu

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="")

Uwaga : ZAWSZE powinieneś umieszczać swoje dane uwierzytelniające AWS ( aws_access_key_idi aws_secret_access_key) w oddzielnym pliku, na przykład:~/.aws/credentials

kev
źródło
Jaka jest odpowiednik lokalizacji systemu Windows dla pliku poświadczeń AWS, ponieważ system Windows nie będzie obsługiwał~
Hamman Samuel
1
@HammanSamuel, możesz to przechowywać jakC:\Users\username\.aws\credentials
kev
2

warto wspomnieć o smart-open, który używa boto3jako zaplecza.

smart-openJest to zamiennik dla Pythona open, który może otwierać pliki z s3, jak również ftp, httpi wiele innych protokołów.

na przykład

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

Poświadczenia aws są ładowane za pośrednictwem poświadczeń boto3 , zwykle pliku w katalogu ~/.aws/lub zmiennej środowiskowej.

Uri Goren
źródło
1
chociaż ta odpowiedź ma charakter informacyjny, nie odpowiada ona na pierwotne pytanie - czyli jakie są odpowiedniki boto3 niektórych metod boto.
robinhood91
2
Smart open używa boto3
Uri Goren
1

Możesz użyć poniższego kodu do napisania, na przykład obrazu do S3 w 2019 roku. Aby móc połączyć się z S3 będziesz musiał zainstalować AWS CLI za pomocą polecenia pip install awscli, a następnie wprowadzić kilka poświadczeń za pomocą polecenia 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
Prateek Bhuwania
źródło