Como escrever um arquivo ou dados para um objeto S3 usando boto3

Respostas:

212

No boto 3, os métodos 'Key.set_contents_from_' foram substituídos por

Por exemplo:

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

Como alternativa, os dados binários podem vir da leitura de um arquivo, conforme descrito nos documentos oficiais comparando boto 2 e boto 3 :

Armazenamento de dados

Armazenar dados de um arquivo, stream ou string é 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'))
jkdev
fonte
botocore.exceptions.NoCredentialsError: Não foi possível localizar as credenciais, como corrigir isso?
deepak murthy
2
@deepakmurthy Não sei por que você está recebendo esse erro ... Você precisaria fazer uma nova pergunta sobre o Stack Overflow e fornecer mais detalhes sobre o problema.
jkdev
1
Quando tento, s3.Object().put()acabo com um objeto com zero content-length. Para mim, put()só aceita dados de string, mas put(str(binarydata)) parece ter algum tipo de problemas de codificação. Acabo com um objeto com aproximadamente 3 vezes o tamanho dos dados originais, o que o torna inútil para mim.
user1129682
@ user1129682 Não sei por que isso acontece. Você poderia fazer uma nova pergunta e fornecer mais detalhes?
jkdev
@jkdev Seria ótimo se você pudesse dar uma olhada .
user1129682
48

boto3 também tem um método para enviar um arquivo diretamente:

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
fonte
5
Isso é bom, mas não permite que os dados atualmente na memória sejam armazenados.
Reid de
3
@Reid: para arquivos na memória, você pode usar o s3.Bucket(...).upload_fileobj()método.
svohara
36

Você não precisa mais converter o conteúdo em binário antes de gravar no arquivo no S3. O exemplo a seguir cria um novo arquivo de texto (chamado newfile.txt) em um intervalo S3 com conteúdo de string:

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
fonte
Não tenho ideia de que minha ação 'colocar' não tem acesso. Eu criei este intervalo e coloquei minha id canônica na lista de acesso.
Chen Lin
Como você dá um prefixneste caso? Ou seja, e se você quiser armazenar o arquivo em my-bucket-name/subfolder/?
kev
3
@kev você pode especificar isso junto com o nome do arquivo 'subpasta / newfile.txt' em vez de 'newfile.txt'
Madhava Carrillo
Re "Você não precisa mais converter o conteúdo em binário antes de gravar no arquivo no S3.", Isso está documentado em algum lugar? Eu estava olhando para boto3.amazonaws.com/v1/documentation/api/latest/reference/… , e pensei que só aceitava bytes. Não tenho certeza do que exatamente constitui um "objeto semelhante a um arquivo procurável", mas não acho que isso inclua strings.
Emma
Eu posso ter comparando isso com download_fileobj () que é para uploads de arquivos multipart grandes. Os métodos de upload requerem objetos de arquivo procuráveis , mas put () permite gravar strings diretamente em um arquivo no depósito, o que é útil para funções lambda para criar e gravar arquivos dinamicamente em um depósito S3.
Franke
28

Aqui está um bom truque para ler JSON 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))

Agora você pode usar json.load_s3e json.dump_s3com a mesma API loadedump

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
fonte
2
Excelente. Para obtê-lo para o trabalho, eu adicionei este bit extra: ...["Body"].read().decode('utf-8').
sedeh
Boa ideia. De qualquer forma, fornece algum espaço para melhorias de nomenclatura.
Jan Vlcinsky
Proposta de reescrita desta bela ideia: gist.github.com/vlcinsky/bbeda4321208aa98745afc29b58e90ac
Jan Vlcinsky
12

Uma versão mais limpa e concisa que uso para fazer upload de arquivos em tempo real para um determinado intervalo S3 e subpasta-

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 : Você deve SEMPRE colocar suas credenciais AWS ( aws_access_key_ide aws_secret_access_key) em um arquivo separado, por exemplo-~/.aws/credentials

kev
fonte
Qual é o local equivalente do Windows para o arquivo de credenciais AWS, já que o Windows não suporta~
Hamman Samuel
1
@HammanSamuel você pode armazená-lo comoC:\Users\username\.aws\credentials
kev
1

vale a pena mencionar o smart-open que usa boto3como back-end.

smart-opené um substituto imediato do python, openque pode abrir arquivos de s3, bem como ftp, httpe muitos outros protocolos.

por exemplo

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

As credenciais do aws são carregadas por meio das credenciais do boto3 , geralmente um arquivo no ~/.aws/diretório ou uma variável de ambiente.

Uri Goren
fonte
1
embora esta resposta seja informativa, não adere à resposta à pergunta original - que é, quais são os equivalentes boto3 de certos métodos boto.
robinhood91 de
1
Smart open usa boto3
Uri Goren,
1

Você pode usar o código a seguir para escrever, por exemplo, uma imagem no S3 em 2019. Para poder se conectar ao S3, você terá que instalar o AWS CLI usando o comando e pip install awscli, em seguida, inserir algumas credenciais usando o 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
Prateek Bhuwania
fonte