storages s3boto3storage files cloudfront aws python django amazon-s3 boto

python - s3boto3storage - Subir imagen disponible en URL pública a S3 usando boto



django-storages (7)

Estoy trabajando en un entorno web de Python y simplemente puedo cargar un archivo desde el sistema de archivos a S3 usando la clave boto.set_contents_from_filename (ruta / a / archivo). Sin embargo, me gustaría subir una imagen que ya está en la web (digamos https://pbs.twimg.com/media/A9h_htACIAAaCf6.jpg:large ).

¿Debería de alguna manera descargar la imagen al sistema de archivos y luego subirla a S3 usando boto como de costumbre, y luego eliminar la imagen?

Lo ideal sería si hay una forma de obtener la clave de boto.set_contents_from_file o algún otro comando que acepte una URL y transmita bien la imagen a S3 sin tener que descargar explícitamente una copia de archivo a mi servidor.

def upload(url): try: conn = boto.connect_s3(settings.AWS_ACCESS_KEY_ID, settings.AWS_SECRET_ACCESS_KEY) bucket_name = settings.AWS_STORAGE_BUCKET_NAME bucket = conn.get_bucket(bucket_name) k = Key(bucket) k.key = "test" k.set_contents_from_file(url) k.make_public() return "Success?" except Exception, e: return e

Utilizando set_contents_from_file, como el anterior, obtengo un error de "cadena de caracteres no tiene atributo ''tell''". Usando set_contents_from_filename con la url, obtengo un error de No such archivo o directory. La documentación de almacenamiento del boto deja de cargar archivos locales y no menciona la carga de archivos almacenados de forma remota.


Así es como lo hice con las requests , la clave es establecer stream=True cuando hace inicialmente la solicitud, y subirlo a s3 usando el método upload.fileobj() :

import requests import boto3 url = "https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg" r = requests.get(url, stream=True) session = boto3.Session() s3 = session.resource(''s3'') bucket_name = ''your-bucket-name'' key = ''your-key-name'' bucket = s3.Bucket(bucket_name) bucket.upload_fileobj(r.raw, key_name)


Con el método boto3 upload_fileobj , puede transmitir un archivo a un depósito S3, sin guardarlo en el disco. Aquí está mi función:

import boto3 import StringIO import contextlib import requests def upload(url): # Get the service client s3 = boto3.client(''s3'') # Rember to se stream = True. with contextlib.closing(requests.get(url, stream=True, verify=False)) as response: # Set up file stream from response content. fp = StringIO.StringIO(response.content) # Upload data to S3 s3.upload_fileobj(fp, ''my-bucket'', ''my-dir/'' + url.split(''/'')[-1])


Desafortunadamente, realmente no hay forma de hacer esto. Al no menos por el momento. Podríamos agregar un método para boto, digamos set_contents_from_url , pero ese método todavía tendría que descargar el archivo a la máquina local y luego cargarlo. Todavía podría ser un método conveniente, pero no te salvaría nada.

Para poder hacer lo que realmente quiere hacer, deberíamos tener alguna capacidad en el servicio S3 en sí que nos permita pasarle la URL y que la almacenemos en un cubo para nosotros. Eso suena como una característica bastante útil. Es posible que desee publicarlo en los foros de S3.


He intentado lo siguiente con boto3 y me funciona:

import boto3; import contextlib; import requests; from io import BytesIO; s3 = boto3.resource(''s3''); s3Client = boto3.client(''s3'') for bucket in s3.buckets.all(): print(bucket.name) url = "@resource url"; with contextlib.closing(requests.get(url, stream=True, verify=False)) as response: # Set up file stream from response content. fp = BytesIO(response.content) # Upload data to S3 s3Client.upload_fileobj(fp, ''aws-books'', ''reviews_Electronics_5.json.gz'')


Ok, desde @garnaat, no suena como que S3 actualmente permite cargas por url. Pude cargar imágenes remotas a S3 leyéndolas en la memoria solamente. Esto funciona.

def upload(url): try: conn = boto.connect_s3(settings.AWS_ACCESS_KEY_ID, settings.AWS_SECRET_ACCESS_KEY) bucket_name = settings.AWS_STORAGE_BUCKET_NAME bucket = conn.get_bucket(bucket_name) k = Key(bucket) k.key = url.split(''/'')[::-1][0] # In my situation, ids at the end are unique file_object = urllib2.urlopen(url) # ''Like'' a file object fp = StringIO.StringIO(file_object.read()) # Wrap object k.set_contents_from_file(fp) return "Success" except Exception, e: return e

También gracias a ¿Cómo puedo crear una instancia de GzipFile del "objeto similar a un archivo" que devuelve urllib.urlopen ()?


Para una respuesta relevante para 2017 a esta pregunta que utiliza el paquete oficial ''boto3'' (en lugar del antiguo paquete ''boto'' de la respuesta original):

Python 3.5

Si tienes una instalación limpia de Python, primero instala los dos paquetes:

pip install boto3

solicitudes de instalación de pip

import boto3 import requests # Uses the creds in ~/.aws/credentials s3 = boto3.resource(''s3'') bucket_name_to_upload_image_to = ''photos'' s3_image_filename = ''test_s3_image.png'' internet_image_url = ''https://docs.python.org/3.7/_static/py.png'' # Do this as a quick and easy check to make sure your S3 access is OK for bucket in s3.buckets.all(): if bucket.name == bucket_name_to_upload_image_to: print(''Good to go. Found the bucket to upload the image into.'') good_to_go = True if not good_to_go: print(''Not seeing your s3 bucket, might want to double check permissions in IAM'') # Given an Internet-accessible URL, download the image and upload it to S3, # without needing to persist the image to disk locally req_for_image = requests.get(internet_image_url, stream=True) file_object_from_req = req_for_image.raw req_data = file_object_from_req.read() # Do the actual upload to s3 s3.Bucket(bucket_name_to_upload_image_to).put_object(Key=s3_image_filename, Body=req_data)


import boto from boto.s3.key import Key from boto.s3.connection import OrdinaryCallingFormat from urllib import urlopen def upload_images_s3(img_url): try: connection = boto.connect_s3(''access_key'', ''secret_key'', calling_format=OrdinaryCallingFormat()) bucket = connection.get_bucket(''boto-demo-1519388451'') file_obj = Key(bucket) file_obj.key = img_url.split(''/'')[::-1][0] fp = urlopen(img_url) result = file_obj.set_contents_from_string(fp.read()) except Exception, e: return e