mirror of
https://github.com/Hopiu/django-imagekit.git
synced 2026-03-17 05:40:25 +00:00
Use hashes for generated image filenames
While this change means users can no longer specify their own filenames, changing a property of a processor, for example, will now result in a new image. This solves a lot of the previous invalidation issues.
This commit is contained in:
parent
a196e00059
commit
7ad5cf4db5
4 changed files with 35 additions and 69 deletions
|
|
@ -3,3 +3,4 @@ from appconf import AppConf
|
|||
|
||||
class ImageKitConf(AppConf):
|
||||
DEFAULT_IMAGE_CACHE_BACKEND = 'imagekit.imagecache.PessimisticImageCacheBackend'
|
||||
CACHE_DIR = 'CACHE/images'
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
from django.conf import settings
|
||||
from hashlib import md5
|
||||
import os
|
||||
import pickle
|
||||
from .lib import StringIO
|
||||
from .processors import ProcessorPipeline
|
||||
from .utils import (img_to_fobj, open_image, IKContentFile, extension_to_format,
|
||||
UnknownExtensionError)
|
||||
suggest_extension, UnknownExtensionError)
|
||||
|
||||
|
||||
class SpecFileGenerator(object):
|
||||
|
|
@ -14,14 +17,18 @@ class SpecFileGenerator(object):
|
|||
self.autoconvert = autoconvert
|
||||
self.storage = storage
|
||||
|
||||
def get_processors(self, source_file):
|
||||
processors = self.processors
|
||||
if callable(processors):
|
||||
processors = processors(source_file)
|
||||
return processors
|
||||
|
||||
def process_content(self, content, filename=None, source_file=None):
|
||||
img = open_image(content)
|
||||
original_format = img.format
|
||||
|
||||
# Run the processors
|
||||
processors = self.processors
|
||||
if callable(processors):
|
||||
processors = processors(source_file)
|
||||
processors = self.get_processors(source_file)
|
||||
img = ProcessorPipeline(processors or []).process(img)
|
||||
|
||||
options = dict(self.options or {})
|
||||
|
|
@ -42,6 +49,25 @@ class SpecFileGenerator(object):
|
|||
content = IKContentFile(filename, imgfile.read(), format=format)
|
||||
return img, content
|
||||
|
||||
def generate_filename(self, source_file):
|
||||
source_filename = source_file.name
|
||||
filename = None
|
||||
if source_filename:
|
||||
hash = md5(''.join([
|
||||
pickle.dumps(self.get_processors(source_file)),
|
||||
self.format,
|
||||
pickle.dumps(self.options),
|
||||
str(self.autoconvert),
|
||||
])).hexdigest()
|
||||
extension = suggest_extension(source_filename, self.format)
|
||||
|
||||
filename = os.path.normpath(os.path.join(
|
||||
settings.IMAGEKIT_CACHE_DIR,
|
||||
os.path.splitext(source_filename)[0],
|
||||
'%s%s' % (hash, extension)))
|
||||
|
||||
return filename
|
||||
|
||||
def generate_file(self, filename, source_file, save=True):
|
||||
"""
|
||||
Generates a new image file by processing the source file and returns
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ class ImageSpecField(object):
|
|||
|
||||
"""
|
||||
def __init__(self, processors=None, format=None, options=None,
|
||||
image_field=None, pre_cache=None, storage=None, cache_to=None,
|
||||
autoconvert=True, image_cache_backend=None):
|
||||
image_field=None, pre_cache=None, storage=None, autoconvert=True,
|
||||
image_cache_backend=None):
|
||||
"""
|
||||
:param processors: A list of processors to run on the original image.
|
||||
:param format: The format of the output file. If not provided,
|
||||
|
|
@ -33,21 +33,6 @@ class ImageSpecField(object):
|
|||
original image.
|
||||
:param storage: A Django storage system to use to save the generated
|
||||
image.
|
||||
:param cache_to: Specifies the filename to use when saving the image
|
||||
cache file. This is modeled after ImageField's ``upload_to`` and
|
||||
can be either a string (that specifies a directory) or a
|
||||
callable (that returns a filepath). Callable values should
|
||||
accept the following arguments:
|
||||
|
||||
- instance -- The model instance this spec belongs to
|
||||
- path -- The path of the original image
|
||||
- specname -- the property name that the spec is bound to on
|
||||
the model instance
|
||||
- extension -- A recommended extension. If the format of the
|
||||
spec is set explicitly, this suggestion will be
|
||||
based on that format. if not, the extension of the
|
||||
original file will be passed. You do not have to use
|
||||
this extension, it's only a recommendation.
|
||||
:param autoconvert: Specifies whether automatic conversion using
|
||||
``prepare_image()`` should be performed prior to saving.
|
||||
:param image_cache_backend: An object responsible for managing the state
|
||||
|
|
@ -71,7 +56,6 @@ class ImageSpecField(object):
|
|||
autoconvert=autoconvert, storage=storage)
|
||||
self.image_field = image_field
|
||||
self.storage = storage
|
||||
self.cache_to = cache_to
|
||||
self.image_cache_backend = image_cache_backend or \
|
||||
get_default_image_cache_backend()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,4 @@
|
|||
import os
|
||||
import datetime
|
||||
|
||||
from django.db.models.fields.files import ImageField, ImageFieldFile
|
||||
from django.utils.encoding import force_unicode, smart_str
|
||||
|
||||
from ...utils import suggest_extension
|
||||
|
||||
|
||||
class ImageSpecFieldFile(ImageFieldFile):
|
||||
|
|
@ -89,52 +83,13 @@ class ImageSpecFieldFile(ImageFieldFile):
|
|||
if save:
|
||||
self.instance.save()
|
||||
|
||||
def _default_cache_to(self, instance, path, specname, extension):
|
||||
"""
|
||||
Determines the filename to use for the transformed image. Can be
|
||||
overridden on a per-spec basis by setting the cache_to property on
|
||||
the spec.
|
||||
|
||||
"""
|
||||
filepath, basename = os.path.split(path)
|
||||
filename = os.path.splitext(basename)[0]
|
||||
new_name = '%s_%s%s' % (filename, specname, extension)
|
||||
return os.path.join('cache', filepath, new_name)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""
|
||||
Specifies the filename that the cached image will use. The user can
|
||||
control this by providing a `cache_to` method to the ImageSpecField.
|
||||
Specifies the filename that the cached image will use.
|
||||
|
||||
"""
|
||||
name = getattr(self, '_name', None)
|
||||
if not name:
|
||||
filename = self.source_file.name
|
||||
new_filename = None
|
||||
if filename:
|
||||
cache_to = self.field.cache_to or self._default_cache_to
|
||||
|
||||
if not cache_to:
|
||||
raise Exception('No cache_to or default_cache_to value'
|
||||
' specified')
|
||||
if callable(cache_to):
|
||||
suggested_extension = suggest_extension(
|
||||
self.source_file.name, self.field.generator.format)
|
||||
new_filename = force_unicode(
|
||||
datetime.datetime.now().strftime(
|
||||
smart_str(cache_to(self.instance,
|
||||
self.source_file.name, self.attname,
|
||||
suggested_extension))))
|
||||
else:
|
||||
dir_name = os.path.normpath(
|
||||
force_unicode(datetime.datetime.now().strftime(
|
||||
smart_str(cache_to))))
|
||||
filename = os.path.normpath(os.path.basename(filename))
|
||||
new_filename = os.path.join(dir_name, filename)
|
||||
|
||||
self._name = new_filename
|
||||
return self._name
|
||||
return self.field.generator.generate_filename(self.source_file)
|
||||
|
||||
@name.setter
|
||||
def name(self, value):
|
||||
|
|
|
|||
Loading…
Reference in a new issue