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

Сохранение изображений и эскизов на s3 в django

Я пытаюсь получить мои изображения с миниатюрами и сохранить их на s3, используя django-storageages, 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

Я работаю с 2 видами:

ADD VIEW: в этом представлении я представляю форму для создания модели с изображением в ней. Изображение загружается на s3. В сигнале post_save я вызываю get_thumbnail() для создания эскиза до его необходимости:

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

DISPLAY VIEW: В этом представлении отображается эскиз, сгенерированный в окне добавления:

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

Без патча:

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

DISPLAY VIEW: все еще всего 3 записи в таблице kvstore, только 1 из кеша, но панель инструментов отладки говорит "установить HTTP-соединение" еще 3 раза

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

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

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

ДОБАВИТЬ ПРОСМОТР: то же, что и выше, но теперь мы доходим до 2 сообщений об установлении HTTP-соединения ПРОСМОТР ДИСПЛЕЯ: то же, что и выше, без каких-либо сообщений о регистрации

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

1304711315.4
_setup
1304711317.84
1304711317.84
_setup
1304711320.3
1304711320.39
_url
1304711323.66

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

https://code.djangoproject.com/ticket/8307 был открыт для решения этой проблемы, но разработчики Django закрыты как wontfix, потому что они хотят, чтобы свойства width и height всегда возвращали true значения. Поэтому я просто 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

Ответ 3

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

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) привести к какой-либо проблеме, но пока что ошибка не возникла.