Django преобразование изображения в webp

У меня есть сервис в приложении моего Django проекта, который загружает изображения, и мне нужно преобразовать все изображения в webp, чтобы оптимизировать дальнейшую работу с этими файлами на стороне фронтенда.

Проект метода _convert_to_webp:

# imports
from pathlib import Path

from django.core.files import temp as tempfile
from django.core.files.uploadedfile import InMemoryUploadedFile
from PIL import Image


# some service class
...
    def _convert_to_webp(self, f_object: InMemoryUploadedFile):
        new_file_name = str(Path(f_object._name).with_suffix('.webp'))
        
        temp_file = tempfile.NamedTemporaryFile(suffix='.temp.webp')
        # FIXME: on other OS may cause FileNotFoundError
        with open(temp_file 'wb') as f:
            for line in f_object.file.readlines():
                ... # will it works good?
        new_file = ...
        
        new_f_object = InMemoryUploadedFile(
            new_file,
            f_object.field_name,
            new_file_name,
            f_object.content_type,
            f_object.size,
            f_object.charset,
            f_object.content_type_extra
        )
        
        return new_file_name, new_f_object
...

f_object это InMemoryUploadedFile экземпляр из тела POST запроса (Django автоматически создает его).

Моя идея состоит в том, чтобы создать временный файл, записать в него данные из f_object.file.readlines(), открыть этот файл с помощью PIL.Image.open и сохранить с помощью format="webp". Хороша ли эта идея или есть другой способ сделать конвертацию файла?

Решение было довольно простым. PIL.Image можно открыть с помощью экземпляра файла, поэтому я просто открыл его с помощью f_object.file, а затем сохранил в экземпляре BytesIO с оптимизацией и сжатием.

Правильно работающий код:

# imports
from pathlib import Path

from django.core.files.uploadedfile import InMemoryUploadedFile
from PIL import Image


# some service class
...
    def _convert_to_webp(self, f_object: InMemoryUploadedFile):
        suffix = Path(f_object._name).suffix
        if suffix == ".webp":
            return f_object._name, f_object
        
        new_file_name = str(Path(f_object._name).with_suffix('.webp'))
        image = Image.open(f_object.file)
        thumb_io = io.BytesIO()
        image.save(thumb_io, 'webp', optimize=True, quality=95)
    
        new_f_object = InMemoryUploadedFile(
            thumb_io,
            f_object.field_name,
            new_file_name,
            f_object.content_type,
            f_object.size,
            f_object.charset,
            f_object.content_type_extra
        )
        
        return new_file_name, new_f_object

95% был выбран в качестве сбалансированного параметра. Было очень плохое качество при qualify=80 или qualify=90.

Я нашел довольно чистый способ сделать это, используя пакет django-resized.

После установки трубопровода мне просто нужно было поменять imageField на ResizedImageField

    img = ResizedImageField(force_format="WEBP", quality=75, upload_to="post_imgs/")

Все загруженные изображения автоматически конвертируются в формат .webp!

Вернуться на верх