FurnitureNear.Me was having some page load issues, mostly due to incredibly large images being used on our search results page, even thought we only needed thumbnail-sized pics on those pages. Yes, we could have used cloudinary, but with 150,000+ images the cost seemed excessive, and existing libraries like sorl-thumbnail aren’t written for Jinja2 and I didn’t feel like doing the port.
I found a great solution for creating thumbnails for ImageFields, and a nice little update that works with existing ImageFields (which we had thousands of), BUT both of these solutions assumed local copies in the filesystem. Um—hello? I have a Macbook Air and barely enough HD space as is—so obviously all assets are on S3.
What’s a girl to do, other that write a modification that pulls the image file via PIL & S3, modifies it, and uploads that magical baby-image to the cloud?
This code also includes improvements to allow for GIFs (original only took JPEG and PNGs.)
Django create thumbnail for existing image stored on S3 (not locally) with support for JPEG, PNG, and GIF
class Image(models.Model): product = models.ForeignKey(Product, related_name='images', null=True, blank=True) image = models.ImageField(upload_to='product-images', max_length=256, null=True, blank=True) image_thumbnail = models.ImageField(upload_to='product-images', max_length=256, null=True, blank=True) img_type = models.CharField(max_length=256) def create_thumbnail(self): # original code for this method came from # http://snipt.net/danfreak/generate-thumbnails-in-django-with-pil/ # and https://gist.github.com/valberg/2429288 # If there is no image associated with this. # do not create thumbnail if not self.image: return from PIL import Image from cStringIO import StringIO from django.core.files.uploadedfile import SimpleUploadedFile import os import urllib # Set our max thumbnail size in a tuple (max width, max height) THUMBNAIL_SIZE = (225, 150) # Open original photo which we want to thumbnail using PIL's Image url = self.image.url try: file=urllib.urlopen(url) # Open the S3 image URL im = StringIO(file.read()) # constructs a StringIO holding the image image = Image.open(im) # Creates PIL Image from URL except: print 'failed on %s' %url #Sometimes it just doesn't work. Deal with it. return PIL_TYPE = image.format print PIL_TYPE # Check PIL Image type to set Django Type and File Extension # Modified from original code to also manage GIFs if PIL_TYPE == 'JPEG': DJANGO_TYPE = 'image/jpeg' FILE_EXTENSION = 'jpg' elif PIL_TYPE == 'PNG': DJANGO_TYPE = 'image/png' FILE_EXTENSION = 'png' elif PIL_TYPE == 'GIF': DJANGO_TYPE = 'image/gif' FILE_EXTENSION = 'gif' # We use our PIL Image object to create the thumbnail, which already # has a thumbnail() convenience method that contrains proportions. # Additionally, we use Image.ANTIALIAS to make the image look better. # Without antialiasing the image pattern artifacts may result. image.thumbnail(THUMBNAIL_SIZE, Image.ANTIALIAS) # Save the thumbnail temp_handle = StringIO() image.save(temp_handle, PIL_TYPE) temp_handle.seek(0) # Save image to a SimpleUploadedFile which can be saved into # ImageField suf = SimpleUploadedFile(os.path.split(self.image.name)[-1], temp_handle.read(), content_type=DJANGO_TYPE) # Save SimpleUploadedFile into image field self.image_thumbnail.save( '%s_thumbnail.%s' % (os.path.splitext(suf.name), FILE_EXTENSION), suf, save=False ) def save(self, *args, **kwargs): self.create_thumbnail() force_update = False # If the instance already has been saved, it has an id and we set # force_update to True if self.id: force_update = True # Force an UPDATE SQL query if we're editing the image to avoid integrity exception super(Image, self).save(force_update=force_update)