Подтвердить что ты не робот

Загрузить изображение, доступное на общедоступном URL-адресе S3, с помощью boto

Я работаю в веб-среде Python, и я могу просто загрузить файл из файловой системы в S3, используя boto key.set_contents_from_filename (путь/в/файл). Однако я хотел бы загрузить изображение, которое уже есть в Интернете (скажем https://pbs.twimg.com/media/A9h_htACIAAaCf6.jpg:large).

Должен ли я каким-то образом загрузить изображение в файловую систему, а затем загрузить его на S3 с помощью boto, как обычно, а затем удалить изображение?

Что было бы идеальным, если есть способ получить файл boto key.set_contents_from_file или какую-либо другую команду, которая будет принимать URL-адрес и красиво передавать изображение на S3 без явной загрузки копии файла на мой сервер.

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

Используя set_contents_from_file, как указано выше, я получаю "строковый объект, не имеющий атрибута" сообщить ". Используя set_contents_from_filename с URL-адресом, я получаю сообщение об ошибке" Нет такого файла или каталога ". документация по хранению boto уходит при загрузке локальных файлов и не упоминает загрузку файлов, хранящихся удаленно.

4b9b3361

Ответ 1

К сожалению, действительно нет никакого способа сделать это. По крайней мере, на данный момент. Мы могли бы добавить метод boto, скажем set_contents_from_url, но этот метод все равно должен был бы загрузить файл на локальный компьютер, а затем загрузить его. Это может быть удобный метод, но это ничего не спасет.

Чтобы делать то, что вы действительно хотите сделать, нам нужно будет иметь некоторые возможности для самой службы S3, которая позволила бы нам передать URL-адрес и сохранить для него URL-адрес для ведра. Это звучит как довольно полезная функция. Возможно, вы захотите опубликовать это на форумах S3.

Ответ 2

Хорошо, из @garnaat, это не похоже на то, что S3 в настоящее время позволяет загружать по URL-адресу. Мне удалось загрузить удаленные изображения на S3, читая их только в памяти. Это работает.

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

Также благодаря Как создать экземпляр GzipFile из "файла-подобного объекта" , который возвращает urllib.urlopen()?

Ответ 3

Для ответа на этот вопрос на 2017 год, в котором используется официальный пакет "boto3" (вместо старого пакета "boto" из исходного ответа):

Python 3.5

Если вы используете чистую установку Python, сначала установите pip оба пакета:

pip install boto3

pip install requests

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)

Ответ 4

Вот как я это сделал с запросами, ключом к которым было установить stream=True при первоначальном выполнении запроса и загрузку в s3 с помощью 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' # key is the name of file on your bucket

bucket = s3.Bucket(bucket_name)
bucket.upload_fileobj(r.raw, key)

Ответ 5

Простая 3-строчная реализация, которая работает на лямбде из коробки:

import boto3
import requests

s3_object = boto3.resource('s3').Object(bucket_name, object_key)

with requests.get(url, stream=True) as r:
    s3_object.put(Body=r.content)

Источник для части .get прямо из документации по requests

Ответ 6

Используя метод boto3 upload_fileobj, вы можете передавать файл в upload_fileobj S3 без сохранения на диск. Вот моя функция:

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

Ответ 7

Я попытался как следует с boto3, и это работает мне:

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

Ответ 8

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

Ответ 9

S3 пока не поддерживает удаленную загрузку. Вы можете использовать приведенный ниже класс для загрузки изображения на S3. Здесь метод загрузки сначала пытается загрузить изображение и некоторое время сохраняет его в памяти, пока оно не будет загружено. Чтобы подключиться к S3, вам необходимо установить интерфейс командной строки AWS с помощью команды pip install awscli, а затем ввести несколько учетных данных с помощью команды 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