Как изменить размер источника с помощью sorl-thumbnail?

Я искал в Интернете свой вопрос и не смог найти четкого ответа ни на один пример.

По сути, я хочу использовать sorl и хочу изменить размер исходного изображения во время сохранения модели, чтобы уменьшить его размер до 640x480, чтобы я не заканчивал тем, что сохранил оригинальные 2,5 МБ файлы пользователя на диске. Затем я буду использовать шаблоны тегов для создания обычных миниатюр из моего источника, как описано в sorl.

Я натолкнулся на несколько источников, ссылающихся на использование поля модели ThumbnailField, которое должно быть доступно в sorl.thumbnail.fields. Смотрите ссылку здесь. Тем не менее, в моей современной копии sorl из ствола я не вижу ни ThumbnailField, ни ImageWithThumbnailsField. Моя попытка импортировать его в модель проваливается соответственно. Я вижу, что эти ссылки старые, и мне интересно, смогу ли я достичь того же с помощью современной технологии sorl.

С другой стороны, в документации sorl указан только ImageField из sorl.thumbnail (см. Здесь), который не имеет аргумента размера для управления изменением размера источника.

Кстати, я вижу, что эта функциональность доступна с easy_thumbnail, который принимает входной параметр source_resize.

Любая помощь будет оценена!

РЕЗЮМЕ

Я принял приведенный ниже ответ, однако я чувствую, что естественная поддержка sorl для этого варианта использования может быть очень полезна - т.е. добавление параметра resize_source в ImageField sorl для разрешения изменения размера исходного изображения. Ниже приведены два фактора, по которым это может быть полезно в данной области:

  1. Не хранить огромные оригинальные изображения пользователя, если оно не требуется для вашего приложения. Экономия дискового пространства.

  2. Не тратьте лишний процессор на изменение размера миниатюр из этих огромных исходных изображений, если у вас нет особых причин высокого качества. Чтобы избежать этого, можно написать вложенные теги в шаблонах в виде миниатюр из изображений меньшего размера, но это может стать раздражающим очень скоро.

3 ответа

Решение

Sorl-х ImageField что вы упоминаете, это просто нормальный Django ImageField с дополнительным преимуществом управления удалением кэшированных миниатюр. Изменение размера при первоначальной загрузке не выполняется - это то, что вы должны реализовать самостоятельно, используя представление, которое вы используете для загрузки. Документы показывают, как это сделать. Вы можете использовать sorl в этом представлении, чтобы выполнить саму фактическую операцию изменения размера, используя API-интерфейсы низкого уровня.

РЕДАКТИРОВАТЬ

Более быстрая альтернатива - просто изменить размер изображения, когда модель сохраняется с помощью sorl. Вы можете сделать что-то вроде следующего (хотя полностью не проверено!)

from sorl.thumbnail import get_thumbnail

class Foo(models.Model):
    image = models.ImageField(upload_to...)

    def save(self, *args, **kwargs):
        if not self.id:
            # Have to save the image (and imagefield) first
            super(Foo, self).save(*args, **kwargs)
            # obj is being created for the first time - resize
            resized = get_thumbnail(self.image, "100x100" ...)
            # Manually reassign the resized image to the image field
            self.image.save(resized.name, resized.read(), True)
        super(Foo, self).save(*args, **kwargs)

это будет означать, что у вас будет 2 версии одного и того же образа на диске - одна, в которой поле изображения django решит сохранить его (upload_to путь), а тот, где уменьшенное изображение сохранено, - уменьшенное изображение. Это, наряду с тем фактом, что изображение загружается и сохраняется дважды, являются недостатками этого подхода. Это быстрее реализовать, хотя

Я обнаружил недостаток в приведенном выше коде: "У str нет метода chunck()", если кто-то захочет его использовать. Вот мое исправление:

    from sorl.thumbnail import get_thumbnail
    from django.core.files.base import ContentFile

 class Foo(models.Model):
    image = models.ImageField(upload_to...)


    def save(self, *args, **kwargs):
        if not self.id:  
            super(Foo, self).save(*args, **kwargs)  
            resized = get_thumbnail(self.image, "100x100" ...)
            self.image.save(resized.name, ContentFile(resized.read()), True)
        super(Foo, self).save(*args, **kwargs)

Некоторое время я искал решение и в конце концов написал приложение django-resized.

В следующем коде используется PIL Engine (часть sorl-thumbnail) для обрезки изображения с именем picture.jpg (проверено с использованием Python 3.8 а также sorl-thumbnail==12.6.3):

#
# Change this import to get the Engine of your underlying libraries.
# Options are: convert_engine, pgmagick_engine, pil_engine, vipsthumbnail_engine or wand_engine.
#
from sorl.thumbnail.engines.pil_engine import Engine

# This object has all we need
engine = Engine()

#
# When receiving data from a request
# you probably have a BytesIO instance ready to use like:
#
#   im = engine.get_image(my_bytes_io)
#
with open("picture.jpg", "rb") as f:
    im = engine.get_image(f)

im_crop = engine.crop(im, (535, 535), options={'crop': 'smart'})

im_crop.save("picture-thumb.jpg")

Вместо изменения saveметода, у меня была бы вспомогательная функция для уменьшения размера изображения (с помощью строк выше) и вызывала ее из представления или формы Django перед обновлением поля изображения. Хотя это сработает наsave сам.

С другой стороны, Engine API имеет больше полезных функций, которые могут быть полезны! Этот API существует с момента первого коммита, поэтому, на мой взгляд, вряд ли он изменится в будущем:create, cropbox, orientation, flip_dimensions, colorspace, remove_border, calculate_scaling_factor, scale, crop, rounded, blur, padding, write, cleanup, get_image_ratio, get_image_info, get_image, get_image_size, is_valid_image.

Другие вопросы по тегам