Хранение изображений и миниатюр на S3 в Django - PullRequest
21 голосов
/ 04 мая 2011

Я пытаюсь получить свои изображения и сохранить их на s3, используя django-хранилища, boto и sorl-thumbnail.У меня это работает, но очень медленно, даже с небольшими изображениями.Я не возражаю, если я сохраняю форму и загружаю изображения на s3 медленно, но мне бы хотелось, чтобы после этого изображение отображалось быстро.

Ответ на этот вопрос SO объясняет, что миниатюране будет создан до первого доступа, но вы можете использовать get_thumbnail (), чтобы создать его заранее.

Django + S3 (boto) + Sorl Thumbnail: Предложения по оптимизации

Я делаю это, и теперь кажется, что все записи в таблицу thumbnail_kvstore созданыпри загрузке изображения, а не когда оно отображается.

Проблема в том, что страница, отображающая изображение, все еще очень медленная.Глядя на панель регистрации на панели инструментов отладки, кажется, что с s3 все еще много общения.Кажется, что после того, как изображение и миниатюры загружены и кэшированы, страница должна отображаться быстро, без связи с s3.

Что я делаю не так?Спасибо!

Обновление : слабый хак, похоже, заработал, но я бы хотел знать, как это сделать правильно:

https://github.com/asciitaxi/sorl-thumbnail/commit/545cce3f5e719a91dd9cc21d78bb973b2211bbbf

Обновление : дополнительная информация для @ sorl

Я работаю с двумя представлениями:

ДОБАВИТЬ ВИД: В этом представлении я отправляю форму для создания моделис изображением в нем.Изображение загружено на s3.В сигнале post_save я вызываю get_thumbnail (), чтобы сгенерировать миниатюру до того, как она понадобится:

im = get_thumbnail(instance.image, '360x360')

DISPLAY VIEW: в этом представлении я отображаю миниатюру, сгенерированную в представлении add:

    {% thumbnail object.image "360x360" as im %}
    <img src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}">
    {% endthumbnail %}

Без патча:

ADD VIEW: создает 3 записи в таблице kvstore, обращается к кэшу 10 раз (6 наборов, 4 получает), на вкладке журналирования панели инструментов отладки 12 раз указано «установление соединения HTTP»

DISPLAY VIEW: по-прежнему всего 3 записи в таблице kvstore, только 1 получают из кэша, но на панели инструментов отладки еще 3 раза сказано «установление HTTP-соединения»

Только с изменением в строке 122:

ДОБАВИТЬ ПРОСМОТР: то же, что и выше, за исключением того, что в журнале только 2 раза указано «установление HTTP-соединения». ПОКАЗЫВАЕТСЯ: то же самое, что и выше, за исключением того, что в журнале только «устанавливается HTTP-соединение» 1 раз

Такжедобавив изменение в строку 118:

ADD VIEW: то же самое, что и выше, но теперь мы дошли до 2 сообщений «Установка HTTP-соединения» DISPLAY VIEW: то же, что и выше, снет сообщений регистрации вообще

ОБНОВЛЕНИЕ : похоже, что storage._setup () вызывается дважды, а storage.url () вызывается один раз.Исходя из времени, я бы сказал, что каждый из них устанавливает соединение с s3:

1304711315.4
_setup
1304711317.84
1304711317.84
_setup
1304711320.3
1304711320.39
_url
1304711323.66

Это, похоже, отражается в логе boto, который говорит "установление HTTP-соединения" 3 раза.

Ответы [ 3 ]

7 голосов
/ 05 мая 2011

Как автор эскиза sorl, я действительно заинтересован в решении этой проблемы, если она не работает так, как я планировал. Если значение ключа sotre заполнено, оно в настоящий момент будет хранить: имя, хранилище и размер. Я сделал предположение, что URL основан на имени и, следовательно, не должен вызывать какие-либо обращения к хранилищу. Глядя на хранилища django, https://github.com/e-loue/django-storages/blob/master/storages/backends/s3boto.py#L214 кажется безопасным предположением. В вашем патче вы по какой-то причине исправили метод read. При создании миниатюры экземпляр ImageFile извлекается из кэша (если не создается), тогда вы, конечно, можете вызвать read, который будет читать файл, но предполагаемое использование - .url, который вызывает url в хранилище с кэшированным именем, которое должно вызывать inturn. быть не доступ к хранилищу op. Не могли бы вы попытаться изолировать вашу проблему, чтобы точно, где в вашем коде происходит доступ к хранилищу?

Также убедитесь, что у вас включен THUMBNAIL_DEBUG и правильно настроено хранилище значений ключей.

2 голосов
/ 09 сентября 2011

Я не уверен, что ваша проблема такая же, как у меня, но я обнаружил, что доступ к свойству width или height обычного Django ImageField будет считывать файл из серверной части хранилища, загружать его в PIL и возвращать размеры оттуда. Это особенно дорого с удаленным бэкэндом, который мы используем, и у нас очень много страниц с большим количеством медиа.

https://code.djangoproject.com/ticket/8307 был открыт для решения этой проблемы, но разработчики Django закрылись как wontfix, потому что они хотят, чтобы свойства width и height всегда возвращали истинные значения. Поэтому я просто monkeypatch _get_image_dimensions (), чтобы использовать эти поля, что предотвращает большое количество сообщений boto и улучшает время загрузки страницы.

Ниже мой код изменен из патча, прикрепленного к этому билету. Я вставил это в место, которое выполняется рано, например, models.py.

from django.core.files.images import ImageFile, get_image_dimensions
def _get_image_dimensions(self):
    from numbers import Number
    if not hasattr(self, '_dimensions_cache'):
        close = self.closed
        if self.field.width_field and self.field.height_field:
            width = getattr(self.instance, self.field.width_field)
            height = getattr(self.instance, self.field.height_field)
            #check if the fields have proper values
            if isinstance(width, Number) and isinstance(height, Number):
                self._dimensions_cache = (width, height)
            else:
                self.open()
                self._dimensions_cache = get_image_dimensions(self, close=close)
        else:
            self.open()
            self._dimensions_cache = get_image_dimensions(self, close=close)

    return self._dimensions_cache
ImageFile._get_image_dimensions = _get_image_dimensions
0 голосов
/ 14 января 2012

Посмотрев на билет @shadfc django, я переопределил обезьян-патч следующим образом:

from django.core.files.images import ImageFile
def _get_image_dimensions(self):
    if not hasattr(self, '_dimensions_cache'):
        if getattr(self.storage, 'IGNORE_IMAGE_DIMENSIONS', False):
            self._dimensions_cache = (0, 0)
        else:
            close = self.closed
            self.open()
            self._dimensions_cache = get_image_dimensions(self, close=close)
    return self._dimensions_cache
ImageFile._get_image_dimensions = _get_image_dimensions

Чтобы использовать его, просто добавьте IGNORE_IMAGE_DIMENSIONS = True в свой класс хранения, и он не будет затронут, чтобы получить размеры изображения. Скорее всего:

from storages.backends.s3boto import S3BotoStorage
S3BotoStorage.IGNORE_IMAGE_DIMENSIONS = True

Мне все еще нужно выяснить, где используются числа, чтобы понять, может ли простое возвращение (0, 0) привести к любой проблеме, но пока нет ошибок.

...