Cómo escribir un archivo o datos en un objeto S3 usando boto3


Respuestas:


212

En el boto 3, los métodos 'Key.set_contents_from_' fueron reemplazados por

Por ejemplo:

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

Alternativamente, los datos binarios pueden provenir de la lectura de un archivo, como se describe en los documentos oficiales que comparan boto 2 y boto 3 :

Almacenamiento de datos

Almacenar datos de un archivo, secuencia o cadena es fácil:

# 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: No se pueden ubicar las credenciales, ¿cómo solucionarlo?
deepak murthy

2
@deepakmurthy No estoy seguro de por qué está recibiendo ese error ... Debería hacer una nueva pregunta de Stack Overflow y proporcionar más detalles sobre el problema.
jkdev

1
Cuando lo intento s3.Object().put()termino con un objeto con cero content-length. Para mí, put()solo acepta datos de cadena, pero put(str(binarydata)) parece tener algún tipo de problemas de codificación. Termino con un objeto aproximadamente 3 veces el tamaño de los datos originales, lo que lo hace inútil para mí.
user1129682

@ user1129682 No estoy seguro de por qué. ¿Podría hacer una nueva pregunta y proporcionar más detalles?
jkdev

@jkdev Sería genial si pudieras echar un vistazo .
user1129682


36

Ya no tiene que convertir el contenido a binario antes de escribir en el archivo en S3. El siguiente ejemplo crea un nuevo archivo de texto (llamado newfile.txt) en un depósito de S3 con contenido de cadena:

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)

No tengo idea de que mi acción 'poner' no tiene acceso. Creé este depósito y puse mi identificación canónica en la lista de acceso.
Chen Lin

¿Cómo se da un prefixen este caso? Es decir, ¿qué pasa si desea almacenar el archivo my-bucket-name/subfolder/?
kev

3
@kev puede especificar eso junto con el nombre de archivo 'subcarpeta / newfile.txt' en lugar de 'newfile.txt'
Madhava Carrillo

Re "Ya no tiene que convertir el contenido a binario antes de escribir en el archivo en S3", ¿está documentado en alguna parte? Estaba mirando boto3.amazonaws.com/v1/documentation/api/latest/reference/… , y pensé que solo aceptaba bytes. No estoy seguro de qué constituye exactamente un "objeto similar a un archivo que se puede buscar", pero no creo que incluya cadenas.
Emma

Es posible que tenga que comparar esto con download_fileobj () que es para cargas de archivos grandes de varias partes. Los métodos de carga requieren objetos de archivo buscables , pero put () le permite escribir cadenas directamente en un archivo en el depósito, lo cual es útil para que las funciones lambda creen y escriban archivos dinámicamente en un depósito S3.
Franke

28

Aquí hay un buen truco para leer JSON desde 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))

Ahora puede usar json.load_s3y json.dump_s3con la misma API que loadydump

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
Excelente. Para conseguir que funcione, he añadido este bit adicional: ...["Body"].read().decode('utf-8').
sede el

Gran idea. De todos modos, proporciona algo de espacio para mejorar los nombres.
Jan Vlcinsky


12

Una versión más limpia y concisa que utilizo para cargar archivos sobre la marcha a un determinado depósito y subcarpeta de 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="")

Nota : SIEMPRE debe poner sus credenciales de AWS ( aws_access_key_idy aws_secret_access_key) en un archivo separado, por ejemplo:~/.aws/credentials


¿Cuál es la ubicación equivalente de Windows para el archivo de credenciales de AWS, ya que Windows no es compatible~
Hamman Samuel

1
@HammanSamuel puedes almacenarlo comoC:\Users\username\.aws\credentials
kev

1

Vale la pena mencionar smart-open que se usa boto3como back-end.

smart-openes un reemplazo directo para python openque puede abrir archivos desde s3, así como ftp,http y muchos otros protocolos.

por ejemplo

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

Las credenciales de aws se cargan a través de las credenciales de boto3 , generalmente un archivo en el ~/.aws/directorio o una variable de entorno.


1
Si bien esta respuesta es informativa, no se adhiere a responder la pregunta original, es decir, cuáles son los equivalentes boto3 de ciertos métodos boto.
robinhood91

1
Smart open usa boto3
Uri Goren

1

Puede usar el siguiente código para escribir, por ejemplo, una imagen en S3 en 2019. Para poder conectarse a S3, tendrá que instalar AWS CLI usando el comando pip install awscli, luego ingrese algunas credenciales usando el comando 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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.