Reputation: 227
I have service in my Django project's app, that upload images, and I need to convert all images to webp to optimize further work with these files on the frontend side.
Draft of _convert_to_webp
method:
# 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
is InMemoryUploadedFile
instance from POST request body (Django automatically create it).
My idea is to create a temporary file, write data from f_object.file.readlines()
to it, open this file with PIL.Image.open
and save with format="webp"
. Is this idea a good one or there is another way to make file converting?
Upvotes: 2
Views: 4936
Reputation: 1139
I just added the following arguments to make it easier to work with other formats
media_path = ResizedImageField(
other_formats=['pdf'], #NEW ARGUMENT
upload_to=document_upload_to,
null=True,
blank=True,
quality=75,
force_format='WEBP',
)
Updates in the ResizedImageFieldFile class of following file
venv/lib/python3.10/site-packages/django_resized/forms.py
class ResizedImageFieldFile(ImageField.attr_class):
def save(self, name, content, save=True):
#NEW LINES
if content.name.lower().endswith(tuple(self.field.other_formats)):
super().save(name, content, save)
return
#NEW LINES END
Updates in the ResizedImageField class of the file venv/lib/python3.10/site-packages/django_resized/forms.py
class ResizedImageField(ImageField):
attr_class = ResizedImageFieldFile
#ADDED other_formats IN THE INIT METHOD
def __init__(self, verbose_name=None, name=None,other_formats=[], **kwargs):
# migrate from 0.2.x
for argname in ('max_width', 'max_height', 'use_thumbnail_aspect_ratio', 'background_color'):
if argname in kwargs:
warnings.warn(
f'Error: Keyword argument {argname} is deprecated for ResizedImageField, '
'see README https://github.com/un1t/django-resized',
DeprecationWarning,
)
del kwargs[argname]
self.other_formats = other_formats #NEW LINE
Upvotes: 0
Reputation: 466
I found a pretty clean way to do this using the django-resized package.
After pip installing, I just needed to swap out the imageField
for a ResizedImageField
img = ResizedImageField(force_format="WEBP", quality=75, upload_to="post_imgs/")
All image uploads are automatically converted to .webp!
Upvotes: 6
Reputation: 227
The solution was pretty simple. PIL.Image
can be opened using file instance, so I just opened it using f_object.file
and then saved it in BytesIO instance with optimization and compression.
Correctly working code:
# 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%
was chosen as balanced parameter. There was very bad quality with quality=80
or quality=90
.
Upvotes: 1