Comment écrire un fichier ou des données sur un objet S3 à l'aide de boto3

Réponses:

212

Dans boto 3, les méthodes 'Key.set_contents_from_' ont été remplacées par

Par exemple:

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

Alternativement, les données binaires peuvent provenir de la lecture d'un fichier, comme décrit dans la documentation officielle comparant boto 2 et boto 3 :

Stocker des données

Le stockage de données à partir d'un fichier, d'un flux ou d'une chaîne est facile:

# 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
la source
botocore.exceptions.NoCredentialsError: Impossible de localiser les informations d'identification comment résoudre ce problème?
deepak murthy
2
@deepakmurthy Je ne sais pas pourquoi vous obtenez cette erreur ... Vous devrez poser une nouvelle question Stack Overflow et fournir plus de détails sur le problème.
jkdev
1
Quand j'essaye, s3.Object().put()je me retrouve avec un objet avec zéro content-length. Pour moi, put()n'accepte que les données de chaîne, mais put(str(binarydata)) semble avoir des problèmes d'encodage. Je me retrouve avec un objet d'environ 3 fois la taille des données d'origine, ce qui le rend inutile pour moi.
user1129682
@ user1129682 Je ne sais pas pourquoi. Pourriez-vous s'il vous plaît poser une nouvelle question et fournir plus de détails?
jkdev
@jkdev Ce serait génial si vous pouviez y jeter un œil .
user1129682
49

boto3 a également une méthode pour télécharger un fichier directement:

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 Abeille
la source
5
C'est bien, mais cela ne permet pas de stocker les données actuellement en mémoire.
Reid
3
@Reid: pour les fichiers en mémoire, vous pouvez utiliser la s3.Bucket(...).upload_fileobj()méthode à la place.
svohara
37

Vous n'avez plus besoin de convertir le contenu en binaire avant d'écrire dans le fichier dans S3. L'exemple suivant crée un nouveau fichier texte (appelé newfile.txt) dans un compartiment S3 avec un contenu de chaîne:

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
la source
Je n'ai aucune idée que mon action «put» n'a pas d'accès. J'ai créé ce bucket et mis mon identifiant canonique sous la liste d'accès.
Chen Lin
Comment donnez-vous un prefixdans ce cas? Signification, que faire si vous souhaitez stocker le fichier my-bucket-name/subfolder/?
kev le
3
@kev, vous pouvez spécifier cela avec le nom de fichier 'subfolder / newfile.txt' au lieu de 'newfile.txt'
Madhava Carrillo
Re "Vous n'avez plus besoin de convertir le contenu en binaire avant d'écrire dans le fichier dans S3.", Est-ce documenté quelque part? Je regardais boto3.amazonaws.com/v1/documentation/api/latest/reference/… , et je pensais qu'il n'acceptait que les octets. Je ne suis pas sûr de ce qui constitue exactement un "objet de type fichier recherché", mais je ne pensais pas que cela incluait des chaînes.
Emma le
J'ai peut-être comparé cela avec download_fileobj () qui est pour les gros téléchargements de fichiers en plusieurs parties. Les méthodes de téléchargement nécessitent des objets de fichier recherchables , mais put () vous permet d'écrire des chaînes directement dans un fichier du compartiment, ce qui est pratique pour les fonctions lambda pour créer et écrire dynamiquement des fichiers dans un compartiment S3.
Franke
28

Voici une belle astuce pour lire JSON à partir de 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))

Vous pouvez maintenant utiliser json.load_s3et json.dump_s3avec la même API que loadetdump

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
la source
2
Excellent. Pour le faire fonctionner, j'ai ajouté ce petit supplément: ...["Body"].read().decode('utf-8').
samedi
Bonne idée. Quoi qu'il en soit, cela offre un espace pour des améliorations de noms.
Jan Vlcinsky le
Réécriture
Jan Vlcinsky le
14

Une version plus claire et concise que j'utilise pour télécharger des fichiers à la volée dans un compartiment et un sous-dossier S3 donnés -

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

Remarque : vous devez TOUJOURS mettre vos informations d'identification AWS ( aws_access_key_idet aws_secret_access_key) dans un fichier séparé, par exemple-~/.aws/credentials

kev
la source
Quel est l'emplacement équivalent Windows pour le fichier d'informations d'identification AWS, puisque Windows ne prend pas en charge~
Hamman Samuel
1
@HammanSamuel vous pouvez le stocker commeC:\Users\username\.aws\credentials
kev
1

il convient de mentionner smart-open qui utilise boto3comme back-end.

smart-openest un remplacement pour son python drop-in openqui peut ouvrir des fichiers à partir s3, ainsi que ftp, httpet bien d' autres protocoles.

par exemple

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

Les informations d'identification aws sont chargées via les informations d'identification boto3 , généralement un fichier dans le répertoire ~/.aws/ou une variable d'environnement.

Uri Goren
la source
1
bien que cette réponse soit informative, elle n'adhère pas à la réponse à la question originale - qui est, quels sont les équivalents boto3 de certaines méthodes boto.
robinhood91
1
Smart open utilise boto3
Uri Goren
1

Vous pouvez utiliser le code ci-dessous pour écrire, par exemple une image sur S3 en 2019. Pour pouvoir vous connecter à S3, vous devrez installer l'AWS CLI à l'aide de la commande pip install awscli, puis entrer quelques informations d'identification à l'aide de la commande 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
la source