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!