Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 
 
 

133 rader
4.4 KiB

  1. import hashlib
  2. import os.path
  3. from io import BytesIO
  4. from django.db import models
  5. from django.core.files.uploadedfile import InMemoryUploadedFile
  6. from django.core.urlresolvers import reverse
  7. from django.dispatch import receiver
  8. import PIL
  9. try:
  10. from importlib import import_module
  11. except ImportError:
  12. from django.utils.importlib import import_module
  13. from . import utils
  14. from .settings import IMAGE_SIZES, IMAGE_PATH, IMAGE_AUTO_DELETE
  15. def hashed_upload_to(instance, filename, **kwargs):
  16. image_type = 'original' if isinstance(instance, Image) else 'thumbnail'
  17. prefix = 'image/%s/by-md5/' % (image_type,)
  18. hasher = hashlib.md5()
  19. for chunk in instance.image.chunks():
  20. hasher.update(chunk)
  21. hash_ = hasher.hexdigest()
  22. base, ext = os.path.splitext(filename)
  23. return '%(prefix)s%(first)s/%(second)s/%(hash)s/%(base)s%(ext)s' % {
  24. 'prefix': prefix,
  25. 'first': hash_[0],
  26. 'second': hash_[1],
  27. 'hash': hash_,
  28. 'base': base,
  29. 'ext': ext,
  30. }
  31. if IMAGE_PATH is None:
  32. upload_to = hashed_upload_to
  33. else:
  34. if callable(IMAGE_PATH):
  35. upload_to = IMAGE_PATH
  36. else:
  37. parts = IMAGE_PATH.split('.')
  38. module_name = '.'.join(parts[:-1])
  39. module = import_module(module_name)
  40. upload_to = getattr(module, parts[-1])
  41. class Image(models.Model):
  42. image = models.ImageField(upload_to=upload_to,
  43. height_field='height', width_field='width',
  44. max_length=255)
  45. height = models.PositiveIntegerField(default=0, editable=False)
  46. width = models.PositiveIntegerField(default=0, editable=False)
  47. def get_by_size(self, size):
  48. return self.thumbnail_set.get(size=size)
  49. def get_absolute_url(self, size=None):
  50. if not size:
  51. return self.image.url
  52. try:
  53. return self.get_by_size(size).image.url
  54. except Thumbnail.DoesNotExist:
  55. return reverse('image-thumbnail', args=(self.id, size))
  56. class ThumbnailManager(models.Manager):
  57. def get_or_create_at_size(self, image_id, size):
  58. image = Image.objects.get(id=image_id)
  59. if size not in IMAGE_SIZES:
  60. raise ValueError("Received unknown size: %s" % size)
  61. try:
  62. thumbnail = image.get_by_size(size)
  63. except Thumbnail.DoesNotExist:
  64. img = utils.scale_and_crop(image.image, **IMAGE_SIZES[size])
  65. # save to memory
  66. buf = BytesIO()
  67. try:
  68. img.save(buf, img.format, **img.info)
  69. except IOError:
  70. if img.info.get('progression'):
  71. orig_MAXBLOCK = PIL.ImageFile.MAXBLOCK
  72. temp_MAXBLOCK = 1048576
  73. if orig_MAXBLOCK >= temp_MAXBLOCK:
  74. raise
  75. PIL.ImageFile.MAXBLOCK = temp_MAXBLOCK
  76. try:
  77. img.save(buf, img.format, **img.info)
  78. finally:
  79. PIL.ImageFile.MAXBLOCK = orig_MAXBLOCK
  80. else:
  81. raise
  82. # and save to storage
  83. original_dir, original_file = os.path.split(image.image.name)
  84. thumb_file = InMemoryUploadedFile(buf, "image", original_file,
  85. None, buf.tell(), None)
  86. thumbnail, created = image.thumbnail_set.get_or_create(
  87. size=size, defaults={'image': thumb_file})
  88. return thumbnail
  89. class Thumbnail(models.Model):
  90. original = models.ForeignKey(Image)
  91. image = models.ImageField(upload_to=upload_to,
  92. height_field='height', width_field='width',
  93. max_length=255)
  94. size = models.CharField(max_length=100)
  95. height = models.PositiveIntegerField(default=0, editable=False)
  96. width = models.PositiveIntegerField(default=0, editable=False)
  97. objects = ThumbnailManager()
  98. class Meta:
  99. unique_together = ('original', 'size')
  100. def get_absolute_url(self):
  101. return self.image.url
  102. @receiver(models.signals.post_save)
  103. def original_changed(sender, instance, created, **kwargs):
  104. if isinstance(instance, Image):
  105. instance.thumbnail_set.all().delete()
  106. @receiver(models.signals.post_delete)
  107. def delete_image_files(sender, instance, **kwargs):
  108. if isinstance(instance, (Image, Thumbnail)) and IMAGE_AUTO_DELETE:
  109. if instance.image.storage.exists(instance.image.name):
  110. instance.image.delete(save=False)