Reputation: 558
i have a website where multiple large size images uploaded everyday after they go live because of heavy size its taking time so i want to generate thumbnail version when image uploaded:
class Image(models.Model):
license_type = (
('Royalty-Free','Royalty-Free'),
('Rights-Managed','Rights-Managed')
)
image_number = models.CharField(default=random_image_number,max_length=12,unique=True)
title = models.CharField(default=random_image_number,max_length = 100)
image = models.ImageField(upload_to = 'image' , default = 'demo/demo.png')
thumbnail = models.ImageField(upload_to='thumbs', editable=False)
category = models.ForeignKey('Category', null=True, blank=True, on_delete=models.CASCADE)
shoot = models.ForeignKey(ImageShoot, on_delete=models.CASCADE, related_name='Image', null=True,blank=True)
image_keyword = models.TextField(max_length=1000)
credit = models.CharField(max_length=150, null=True)
location = models.CharField(max_length=100, null=True)
license_type = models.CharField(max_length=20,choices=license_type, default='')
uploaded_at = models.TimeField(auto_now_add=True)
def __str__(self):
return self.title
def save(self, *args, **kwargs):
super(Image, self).save(*args, **kwargs)
if not self.make_thumbnail():
raise Exception('Could not create thumbnail - is the file type valid?')
def make_thumbnail(self):
fh = storage.open(self.Image.name)
try:
image = PILImage.open(fh)
except:
return False
image.thumbnail((400,400),PILImage.ANTIALIAS)
fh.close()
thumb_name, thumb_extension = os.path.splitext(self.Image.name)
thumb_extension = thumb_extension.lower()
thumb_filename = thumb_name + '_thumb' + thumb_extension
if thumb_extension in ['.jpg', '.jpeg']:
FTYPE = 'JPEG'
elif thumb_extension == '.gif':
FTYPE = 'GIF'
elif thumb_extension == '.png':
FTYPE = 'PNG'
else:
return False # Unrecognized file type
# Save thumbnail to in-memory file as StringIO
temp_thumb = StringIO()
image.save(temp_thumb, FTYPE)
temp_thumb.seek(0)
# Load a ContentFile into the thumbnail field so it gets saved
self.thumbnail.save(thumb_filename, ContentFile(temp_thumb.read()), save=True)
temp_thumb.close()
return True
admin.py:
@admin.register(Image)
class ImageAdmin(admin.ModelAdmin):
readonly_fields=['image_number','uploaded_at']
fields = ['title','image_number','shoot','category',
'image','image_keyword','credit','license_type','location','uploaded_at']
now this is the error:
string argument expected, got 'bytes'
backtrace:
File "/home/tboss/Desktop/environment/live/backend/venv/lib/python3.7/site-packages/PIL/JpegImagePlugin.py", line 779, in _save
ImageFile._save(im, fp, [("jpeg", (0, 0) + im.size, 0, rawmode)], bufsize)
File "/home/tboss/Desktop/environment/live/backend/venv/lib/python3.7/site-packages/PIL/ImageFile.py", line 513, in _save
fp.write(d)
TypeError: string argument expected, got 'bytes'
when i user BytesIO:
i get this error:
maximum recursion depth exceeded in comparison
backtrace:
File "/home/tboss/Desktop/environment/live/backend/venv/lib/python3.7/site-packages/PIL/TiffImagePlugin.py", line 319, in __init__
if isinstance(value, Fraction):
File "/home/tboss/Desktop/environment/live/backend/venv/lib/python3.7/abc.py", line 139, in __instancecheck__
return _abc_instancecheck(cls, instance)
RecursionError: maximum recursion depth exceeded in comparison
i am current uploading image from admin but also going to using drf. currently its not creating thumbnail.............................................................................................................
Upvotes: 1
Views: 5411
Reputation: 310
Best way is to use Django Advance Thumbnail. Here's a basic example of how to use the AdvanceDJThumbnailField in a model:
from django.db import models
from django_advance_thumbnail import AdvanceThumbnailField
class MyModel(models.Model):
my_image = models.ImageField(upload_to='images/', null=True, blank=True)
my_image_thumbnail = AdvanceThumbnailField(source_field='my_image', upload_to='images/thumbnails/', null=True, blank=True,
size=(300, 300))
In this example, AdvanceDJThumbnailField
is used to create a thumbnail
from the image
field. Whenever an image is uploaded
or updated
, a corresponding thumbnail is automatically
generated
and stored in the thumbnail field. The thumbnail's dimensions are determined by the optional size parameter, which defaults to (300, 300) if not specified.
This setup ensures that the lifecycle of the thumbnail is tied to its source image. If the source image is deleted, the associated thumbnail is also removed. This seamless synchronization simplifies image management in your Django models.
Installation of Django Advance Thumbnail.
pip install django_advance_thumbnail
Add django_advance_thumbnail
to your INSTALLED_APPS
in settings.py
INSTALLED_APPS = [
# ...
'django_advance_thumbnail',
# ...
]
Upvotes: -1
Reputation: 1430
As said in the comment, you can work with base64 encoded image instead. In order to do this, first change the thumbnail field to the following:
thumbnail = models.CharField(max_length=2000, blank=True, null=True)
Then, override the model save method and add the following code:
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
if not self.image:
self.thumbnail = None
else:
thumbnail_size = 50, 50
data_img = BytesIO()
tiny_img = Image.open(self.image)
tiny_img.thumbnail(thumbnail_size)
tiny_img.save(data_img, format="BMP")
tiny_img.close()
try:
self.thumbnail = "data:image/jpg;base64,{}".format(
base64.b64encode(data_img.getvalue()).decode("utf-8")
)
except UnicodeDecodeError:
self.blurred_image = None
super(Image, self).save(force_insert, force_update, using, update_fields)
You can change the thumbnail_size variable to match your needs. Lastly, add the following to your import section:
import base64
from io import BytesIO
Don't forget to run makemigrations and migrate commands! Let me know if you have any problems.
Upvotes: 3