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