|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- import hashlib
- import os.path
- from io import BytesIO
-
- from django.db import models
- from django.core.files.uploadedfile import InMemoryUploadedFile
- from django.core.urlresolvers import reverse
- from django.dispatch import receiver
- import PIL
-
- try:
- from importlib import import_module
- except ImportError:
- from django.utils.importlib import import_module
-
- from . import utils
- from .settings import IMAGE_SIZES, IMAGE_PATH, IMAGE_AUTO_DELETE
-
-
- def hashed_upload_to(instance, filename, **kwargs):
- image_type = 'original' if isinstance(instance, Image) else 'thumbnail'
- prefix = 'image/%s/by-md5/' % (image_type,)
- hasher = hashlib.md5()
- for chunk in instance.image.chunks():
- hasher.update(chunk)
- hash_ = hasher.hexdigest()
- base, ext = os.path.splitext(filename)
- return '%(prefix)s%(first)s/%(second)s/%(hash)s/%(base)s%(ext)s' % {
- 'prefix': prefix,
- 'first': hash_[0],
- 'second': hash_[1],
- 'hash': hash_,
- 'base': base,
- 'ext': ext,
- }
-
-
- if IMAGE_PATH is None:
- upload_to = hashed_upload_to
- else:
- if callable(IMAGE_PATH):
- upload_to = IMAGE_PATH
- else:
- parts = IMAGE_PATH.split('.')
- module_name = '.'.join(parts[:-1])
- module = import_module(module_name)
- upload_to = getattr(module, parts[-1])
-
-
- class Image(models.Model):
- image = models.ImageField(upload_to=upload_to,
- height_field='height', width_field='width',
- max_length=255)
- height = models.PositiveIntegerField(default=0, editable=False)
- width = models.PositiveIntegerField(default=0, editable=False)
-
- def get_by_size(self, size):
- return self.thumbnail_set.get(size=size)
-
- def get_absolute_url(self, size=None):
- if not size:
- return self.image.url
- try:
- return self.get_by_size(size).image.url
- except Thumbnail.DoesNotExist:
- return reverse('image-thumbnail', args=(self.id, size))
-
-
- class ThumbnailManager(models.Manager):
- def get_or_create_at_size(self, image_id, size):
- image = Image.objects.get(id=image_id)
- if size not in IMAGE_SIZES:
- raise ValueError("Received unknown size: %s" % size)
- try:
- thumbnail = image.get_by_size(size)
- except Thumbnail.DoesNotExist:
- img = utils.scale_and_crop(image.image, **IMAGE_SIZES[size])
- # save to memory
- buf = BytesIO()
- try:
- img.save(buf, img.format, **img.info)
- except IOError:
- if img.info.get('progression'):
- orig_MAXBLOCK = PIL.ImageFile.MAXBLOCK
- temp_MAXBLOCK = 1048576
- if orig_MAXBLOCK >= temp_MAXBLOCK:
- raise
- PIL.ImageFile.MAXBLOCK = temp_MAXBLOCK
- try:
- img.save(buf, img.format, **img.info)
- finally:
- PIL.ImageFile.MAXBLOCK = orig_MAXBLOCK
- else:
- raise
- # and save to storage
- original_dir, original_file = os.path.split(image.image.name)
- thumb_file = InMemoryUploadedFile(buf, "image", original_file,
- None, buf.tell(), None)
- thumbnail, created = image.thumbnail_set.get_or_create(
- size=size, defaults={'image': thumb_file})
- return thumbnail
-
-
- class Thumbnail(models.Model):
- original = models.ForeignKey(Image)
- image = models.ImageField(upload_to=upload_to,
- height_field='height', width_field='width',
- max_length=255)
- size = models.CharField(max_length=100)
- height = models.PositiveIntegerField(default=0, editable=False)
- width = models.PositiveIntegerField(default=0, editable=False)
-
- objects = ThumbnailManager()
-
- class Meta:
- unique_together = ('original', 'size')
-
- def get_absolute_url(self):
- return self.image.url
-
-
- @receiver(models.signals.post_save)
- def original_changed(sender, instance, created, **kwargs):
- if isinstance(instance, Image):
- instance.thumbnail_set.all().delete()
-
-
- @receiver(models.signals.post_delete)
- def delete_image_files(sender, instance, **kwargs):
- if isinstance(instance, (Image, Thumbnail)) and IMAGE_AUTO_DELETE:
- if instance.image.storage.exists(instance.image.name):
- instance.image.delete(save=False)
|