def _get_geometry(self, image_file): image = ImageFile(image_file) source_image = default.engine.get_image(image) size = default.engine.get_image_size(source_image) print "GEOM: %sx%s" % (str(size[0]), str(size[1])) image.set_size(size) exif_rotated = False if hasattr(source_image, '_getexif'): exif = source_image._getexif() orientation = exif and exif.get(self.EXIF_ORIENTATION_TAG, 0) exif_rotated = orientation in (6, 8) actual_size = exif_rotated and size[::-1] or size width, height = actual_size if width >= height: height = height*self.DEFAULT_WIDTH/width width = self.DEFAULT_WIDTH else: width = width*self.DEFAULT_WIDTH/height height = self.DEFAULT_WIDTH return width, height
def get_thumbnail(self, file_, geometry_string, **options): """ Returns thumbnail as an ImageFile instance for file with geometry and options given. First it will try to get it from the key value store, secondly it will create it. """ source = ImageFile(file_) for key, value in self.default_options.iteritems(): options.setdefault(key, value) name = self._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) cached = default.kvstore.get(thumbnail) if cached: return cached if not thumbnail.exists(): # We have to check exists() because the Storage backend does not # overwrite in some implementations. source_image = default.engine.get_image(source) # We might as well set the size since we have the image in memory size = default.engine.get_image_size(source_image) source.set_size(size) self._create_thumbnail(source_image, geometry_string, options, thumbnail) # If the thumbnail exists we don't create it, the other option is # to delete and write but this could lead to race conditions so I # will just leave that out for now. default.kvstore.get_or_set(source) default.kvstore.set(thumbnail, source) return thumbnail
def _create_alternative_resolutions(self, source_image, geometry_string, options, name): """ Creates the thumbnail by using default.engine with multiple output sizes. Appends @<ratio>x to the file name. """ if not options['alternative_resolutions']: return ratio = default.engine.get_image_ratio(source_image) geometry = parse_geometry(geometry_string, ratio) file_type = name.split('.')[len(name.split('.')) - 1] for resolution in options['alternative_resolutions']: resolution_geometry = (int(geometry[0] * resolution), int(geometry[1] * resolution)) resolution_options = options.copy() if 'crop' in options and isinstance(options['crop'], basestring): crop = options['crop'].split(" ") for i in xrange(len(crop)): s = re.match("(\d+)px", crop[i]) if s: crop[i] = "%spx" % int(int(s.group(1)) * resolution) resolution_options['crop'] = " ".join(crop) thumbnail_name = name.replace(".%s" % file_type, "@%sx.%s" % (resolution, file_type)) image = default.engine.create(source_image, resolution_geometry, resolution_options) thumbnail = ImageFile(thumbnail_name, default.storage) default.engine.write(image, resolution_options, thumbnail) size = default.engine.get_image_size(image) thumbnail.set_size(size)
def _create_alternative_resolutions(self, source_image, geometry_string, options, name): """ Creates the thumbnail by using default.engine with multiple output sizes. Appends @<ratio>x to the file name. """ ratio = default.engine.get_image_ratio(source_image, options) geometry = parse_geometry(geometry_string, ratio) file_name, dot_file_ext = os.path.splitext(name) for resolution in settings.THUMBNAIL_ALTERNATIVE_RESOLUTIONS: resolution_geometry = (int(geometry[0] * resolution), int(geometry[1] * resolution)) resolution_options = options.copy() if 'crop' in options and isinstance(options['crop'], string_types): crop = options['crop'].split(" ") for i in range(len(crop)): s = re.match("(\d+)px", crop[i]) if s: crop[i] = "%spx" % int(int(s.group(1)) * resolution) resolution_options['crop'] = " ".join(crop) image = default.engine.create(source_image, resolution_geometry, options) thumbnail_name = '%(file_name)s%(suffix)s%(file_ext)s' % { 'file_name': file_name, 'suffix': '@%sx' % resolution, 'file_ext': dot_file_ext } thumbnail = ImageFile(thumbnail_name, default.storage) default.engine.write(image, resolution_options, thumbnail) size = default.engine.get_image_size(image) thumbnail.set_size(size)
def test_storage_serialize(self): im = ImageFile(Item.objects.get(image="500x500.jpg").image) self.assertEqual(im.serialize_storage(), "thumbnail_tests.storage.TestStorage") self.assertEqual(ImageFile("http://www.image.jpg").serialize_storage(), "sorl.thumbnail.images.UrlStorage") self.assertEqual( ImageFile("http://www.image.jpg", default.storage).serialize_storage(), "thumbnail_tests.storage.TestStorage", ) self.assertEqual(ImageFile("getit", default_storage).serialize_storage(), "thumbnail_tests.storage.TestStorage")
def delete(self, file_, delete_file=True): """ Deletes file_ references in Key Value store and optionally the file_ it self. """ image_file = ImageFile(file_) if delete_file: image_file.delete() default.kvstore.delete(image_file)
def get_thumbnail(self, file_, geometry_string, **options): """ Returns thumbnail as an ImageFile instance for file with geometry and options given. First it will try to get it from the key value store, secondly it will create it. """ logger.debug('Getting thumbnail for file [%s] at [%s]', file_, geometry_string) source = ImageFile(file_) #preserve image filetype if settings.THUMBNAIL_PRESERVE_FORMAT: self.default_options['format'] = self._get_format(file_) for key in self.default_options.keys(): if not key in options: options.update({key: self.default_options[key]}) # For the future I think it is better to add options only if they # differ from the default settings as below. This will ensure the same # filenames being generated for new options at default. for key, attr in self.extra_options: value = getattr(settings, attr) if value != getattr(default_settings, attr): options.setdefault(key, value) name = self._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) cached = default.kvstore.get(thumbnail) if cached: return cached else: # We have to check exists() because the Storage backend does not # overwrite in some implementations. # so we make the assumption that if the thumbnail is not cached, it doesn't exist try: source_image = default.engine.get_image(source) except IOError: # if S3Storage says file doesn't exist remotely, don't try to # create it, exit early # Will return working empty image type; 404'd image logger.warn('Remote file [%s] at [%s] does not exist', file_, geometry_string) return thumbnail # We might as well set the size since we have the image in memory size = default.engine.get_image_size(source_image) source.set_size(size) self._create_thumbnail(source_image, geometry_string, options, thumbnail) # If the thumbnail exists we don't create it, the other option is # to delete and write but this could lead to race conditions so I # will just leave that out for now. default.kvstore.get_or_set(source) default.kvstore.set(thumbnail, source) return thumbnail
def get_thumbnail(self, file_, geometry_string, **options): """ Returns thumbnail as an ImageFile instance for file with geometry and options given. First it will try to get it from the key value store, secondly it will create it. """ if not options.get('format'): ext = str(file_).split('.')[-1].lower() options['format'] = ext.upper() if ext in AUTO_FORMATS else settings.THUMBNAIL_FORMAT source = ImageFile(file_) for key, value in self.default_options.items(): options.setdefault(key, value) # For the future I think it is better to add options only if they # differ from the default settings as below. This will ensure the same # filenames beeing generated for new options at default. for key, attr in self.extra_options: value = getattr(settings, attr) if value != getattr(default_settings, attr): options.setdefault(key, value) name = self._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) cached = default.kvstore.get(thumbnail) if cached: return cached if not thumbnail.exists(): # We have to check exists() because the Storage backend does not # overwrite in some implementations. try: source_image = default.engine.get_image(source) except IOError: if settings.THUMBNAIL_IMAGE_MISSING_DUMMY: return DummyImageFile(geometry_string) else: raise # We might as well set the size since we have the image in memory size = default.engine.get_image_size(source_image) source.set_size(size) self._create_thumbnail(source_image, geometry_string, options, thumbnail) self._create_alternative_resolutions(source, geometry_string, options, thumbnail.name) # If the thumbnail exists we don't create it, the other option is # to delete and write but this could lead to race conditions so I # will just leave that out for now. default.kvstore.get_or_set(source) default.kvstore.set(thumbnail, source) return thumbnail
def test_storage_serialize(self): im = ImageFile(Item.objects.get(image='500x500.jpg').image) self.assertEqual(im.serialize_storage(), 'thumbnail_tests.storage.TestStorage') self.assertEqual( ImageFile('http://www.image.jpg').serialize_storage(), 'sorl.thumbnail.images.UrlStorage', ) self.assertEqual( ImageFile('http://www.image.jpg', default.storage).serialize_storage(), 'thumbnail_tests.storage.TestStorage', ) self.assertEqual( ImageFile('getit', default_storage).serialize_storage(), 'thumbnail_tests.storage.TestStorage', )
def create_thumbnail(image_file, geometry_string, **options): # Note that thumbnail options must be same for a type of thumbnail. # Otherwise, different thumbnails are created. source = ImageFile(image_file) for key, value in default.backend.default_options.items(): options.setdefault(key, value) name = default.backend._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) source_image = default.engine.get_image(source) default.backend._create_thumbnail(source_image, geometry_string, options, thumbnail) # Need to set size to store in kvstore. size = default.engine.get_image_size(source_image) source.set_size(size) default.kvstore.get_or_set(source) default.kvstore.set(thumbnail, source)
def create_thumbnail(file_, geometry_string, options, name, force=False): if file_: source = ImageFile(file_) else: return thumbnail = ImageFile(name, default.storage) # We have to check exists() because the Storage backend does not # overwrite in some implementations. if settings.THUMBNAIL_FORCE_OVERWRITE or not thumbnail.exists() or force: try: source_image = default.engine.get_image(source) except IOError as e: logger.exception(e) # if S3Storage says file doesn't exist remotely, don't try to # create it and exit early. # Will return working empty image type; 404'd image logger.warn( text_type('Remote file [%s] at [%s] does not exist'), file_, geometry_string) return # We might as well set the size since we have the image in memory try: image_info = default.engine.get_image_info(source_image) options['image_info'] = image_info except AttributeError: options['image_info'] = {} size = default.engine.get_image_size(source_image) source.set_size(size) try: default.backend._create_thumbnail( source_image, geometry_string, options, thumbnail) default.backend._create_alternative_resolutions( source_image, geometry_string, options, thumbnail.name) finally: default.engine.cleanup(source_image) # If the thumbnail exists we don't create it, the other option is # to delete and write but this could lead to race conditions so I # will just leave that out for now. default.kvstore.get_or_set(source) default.kvstore.set(thumbnail, source)
def get_thumbnail(self, file_, geometry_string, **options): """ Returns thumbnail as an ImageFile instance for file with geometry and options given. First it will try to get it from the key value store, secondly it will create it. """ source = ImageFile(file_) for key, value in self.default_options.iteritems(): options.setdefault(key, value) options['mtime'] = os.path.getmtime(source.storage.path(source))###customization name = self._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) cached = default.kvstore.get(thumbnail) if cached and cached.exists():###customization return cached if not thumbnail.exists(): # We have to check exists() because the Storage backend does not # overwrite in some implementations. source_image = default.engine.get_image(source) size = default.engine.get_image_size(source_image) if options.get('autocrop', None): source_image = autocrop(source_image, geometry_string, options) # We might as well set the size since we have the image in memory size = default.engine.get_image_size(source_image) source.set_size(size) ### customization: race condition, do not raise an OSError when the dir exists. # see sorl.thumbnail.images.ImageFile.write, it's not safe to simply throw # /sub/dir/name.jpg to django.core.files.storage.FileSystemStorage._save full_path = thumbnail.storage.path(name) directory = os.path.dirname(full_path) if not os.path.exists(directory): try: os.makedirs(directory) except OSError: pass ### end of customization self._create_thumbnail(source_image, geometry_string, options, thumbnail) # If the thumbnail exists we don't create it, the other option is # to delete and write but this could lead to race conditions so I # will just leave that out for now. default.kvstore.get_or_set(source) default.kvstore.set(thumbnail, source) return thumbnail
def get_thumbnail(self, file_, geometry_string, force_create=True, **options): """ Returns thumbnail as an ImageFile instance for file with geometry and options given. First it will try to get it from the key value store, secondly it will create it. """ source = ImageFile(file_) if settings.THUMBNAIL_PRESERVE_FORMAT: format = self._get_format(file_) if format == "GIF": options.setdefault("format", format) for key, value in self.default_options.iteritems(): options.setdefault(key, value) # For the future I think it is better to add options only if they # differ from the default settings as below. This will ensure the same # filenames beeing generated for new options at default. for key, attr in self.extra_options: value = getattr(settings, attr) if value != getattr(default_settings, attr): options.setdefault(key, value) name = self._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) cached = default.kvstore.get(thumbnail) if cached: return cached if not force_create: return source if not thumbnail.exists(): # We have to check exists() because the Storage backend does not # overwrite in some implementations. source_image = default.engine.get_image(source) # We might as well set the size since we have the image in memory size = default.engine.get_image_size(source_image) source.set_size(size) self._create_thumbnail(source_image, geometry_string, options, thumbnail) # If the thumbnail exists we don't create it, the other option is # to delete and write but this could lead to race conditions so I # will just leave that out for now. default.kvstore.get_or_set(source) default.kvstore.set(thumbnail, source) return thumbnail
def thumbnail(request, root_dir=None): """ Scales (conserving aspect ratio) a requested image with given width, height and orientation """ width = request.GET.get('width', None) height = request.GET.get('height', None) crop = request.GET.get('crop', None) orientation = request.GET.get('orientation', constants.ORIENTATION_DEFAULT) geometry_string = make_geometry_string(width, height) if root_dir is None: root_dir = settings.IMAGERESIZER_ROOT resource_path = request.path.replace( settings.IMAGERESIZER_PATH_TO_REMOVE, '', 1 ) path = os.path.realpath(os.path.join(root_dir, resource_path)) if not os.path.exists(path): raise Http404 if geometry_string is None: image = ImageFile(path, storage=default.storage) image.set_size() geometry_string = make_geometry_string(image.width, image.height) thumbnail = get_thumbnail(path, geometry_string, ll_transverse=True \ if orientation == constants.ORIENTATION_LANDSCAPE \ else False) mimetype, encoding = mimetypes.guess_type(path) response = HttpResponse(thumbnail.read(), mimetype=mimetype) last_modified = http_date( time.mktime( thumbnail.storage.modified_time(thumbnail.name).timetuple() ) ) response['Last-Modified'] = last_modified response['ETag'] = quote_etag(hashlib.sha1(last_modified).hexdigest()) response['Content-Length'] = thumbnail.storage.size(thumbnail.name) response['Accept-Ranges'] = 'bytes' return response
def get_thumbnail(self, file_, geometry_string, **options): """ Returns thumbnail as an ImageFile instance for file with geometry and options given. First it will try to get it from the key value store, secondly it will create it. """ source = ImageFile(file_) for key, value in self.default_options.iteritems(): options.setdefault(key, value) # For the future I think it is better to add options only if they # differ from the default settings as below. This will ensure the same # filenames beeing generated for new options at default. for key, attr in self.extra_options: value = getattr(settings, attr) if value != getattr(default_settings, attr): options.setdefault(key, value) name = self._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) cached = default.kvstore.get(thumbnail) if cached: return cached if not thumbnail.exists(): # We have to check exists() because the Storage backend does not # overwrite in some implementations. try: source_image = default.engine.get_image(source) # We might as well set the size since we have the image in memory size = default.engine.get_image_size(source_image) source.set_size(size) self._create_thumbnail(source_image, geometry_string, options, thumbnail) self._create_alternative_resolutions(source_image, geometry_string, options, thumbnail.name) except IOError, e: if settings.THUMBNAIL_DUMMY: return DummyImageFile(geometry_string) else: raise e
def _clean_image(self): from sorl.thumbnail.engines.pil_engine import Engine from sorl.thumbnail.images import ImageFile e = Engine() f = ImageFile(self.image.file) tmp_image = e.get_image(f) new_file = BytesIO() tmp_image = tmp_image.convert("RGB") tmp_image.save(new_file, "jpeg") file_content = ContentFile(new_file.getvalue()) self.image.save(self.image.name, file_content, save=False)
def _create_alternative_resolutions(self, source, geometry_string, options, name): """ Creates the thumbnail by using default.engine with multiple output sizes. Appends @<ratio>x to the file name. """ if not options['alternative_resolutions']: return source_image = default.engine.get_image(source) ratio = default.engine.get_image_ratio(source_image, options) geometry_orig = parse_geometry(geometry_string, ratio) file_type = name.split('.')[len(name.split('.'))-1] for res in options['alternative_resolutions']: geometry = (toint(geometry_orig[0]*res), toint(geometry_orig[1]*res)) thumbnail_name = name.replace(".%s" % file_type, "@%sx.%s" % (res, file_type)) source_image = default.engine.get_image(source) image = default.engine.create(source_image, geometry, options) thumbnail = ImageFile(thumbnail_name, default.storage) default.engine.write(image, options, thumbnail) size = default.engine.get_image_size(image) thumbnail.set_size(size)
def optimized_get_thumbnail(file_, all_sizes): """ This function is based on: `sorl.thumbnail.base.ThumbnailBackend.get_thumbnail` but optimized to download the image file only once from the backend and generate all thumbnails in memory. If we called .get_thumbnail() for each size, we would do a separate download for each rendering which takes too long. We are not checking if the image is cached. Always generate the thumb in this call. We also assume that the thumbnail does not exist for overwriting """ from sorl.thumbnail.images import ImageFile from sorl.thumbnail import default backend = default.backend source = ImageFile(file_) # Here we call .get_image() once, we'll use this for all thumbs source_image = default.engine.get_image(source) for thumb_opts in all_sizes: geometry_string = helpers.size_to_str(thumb_opts['size']) # .copy() opts so settings doesn't get updated!! options = thumb_opts.get('options', {}).copy() for key, value in backend.default_options.iteritems(): options.setdefault(key, value) name = backend._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) size = default.engine.get_image_size(source_image) source.set_size(size) backend._create_thumbnail(source_image, geometry_string, options, thumbnail) default.kvstore.get_or_set(source) default.kvstore.set(thumbnail, source)
def create_thumbnail(file_, geometry_string, options, name): thumbnail = ImageFile(name, default.storage) if not thumbnail.exists(): source = ImageFile(file_) source_image = default.engine.get_image(source) size = default.engine.get_image_size(source_image) source.set_size(size) default.backend._create_thumbnail(source_image, geometry_string, options, thumbnail) # Need to update both the source and the thumbnail with correct sizing default.kvstore.set(source) default.kvstore.set(thumbnail, source)
def get_thumbnail(self, file_, geometry_string, **options): source = ImageFile(file_) for key, value in self.default_options.iteritems(): options.setdefault(key, value) for key, attr in self.extra_options: value = getattr(settings, attr) if value != getattr(default_settings, attr): options.setdefault(key, value) # Generate a name for the thumbnail name = self._get_thumbnail_filename(source, geometry_string, options) # See if we've got a hit in the cache thumbnail = ImageFile(name, default.storage) cached = default.kvstore.get(thumbnail) if cached: return cached # We cannot check if the file exists, as remote storage is slow. If # we have reached this point, the image does not exist in our kvstore # so create the entry and queue the generation of the image. # # Note: If the thumbnail file has been deleted, you will need to manually # clear the corresponding row from the kvstore to have thumbnail rebuilt. job = create_thumbnail.delay(file_, geometry_string, options, name) if job: geometry = parse_geometry(geometry_string) # We can't add a source row to the kvstore without the size # information being looked up, so add dummy information here # We'll need to correct this information when we generate the thumbnail source.set_size(geometry) default.kvstore.get_or_set(source) # We don't want to do any file access in this thread, so we tell sorlery # to proceed as normal and cheekily update the name and storage after # the hash has been calculated. thumbnail.set_size(geometry) default.kvstore.set(thumbnail, source) # Now we go back and manually update the thumbnail to point at the source image # Hopefully someone can suggest a better way to do this ... but the sorl internals # don't make it easy to. rawvalue = default.kvstore._get_raw(add_prefix(thumbnail.key)) rawvaluearr = deserialize(rawvalue) rawvaluearr['name'] = file_.name default.kvstore._set_raw(add_prefix(thumbnail.key), serialize(rawvaluearr)) thumbnail.name = file_.name return thumbnail
def test_kvstore(self): im = ImageFile(Item.objects.get(image='500x500.jpg').image) self.KVSTORE.delete_thumbnails(im) th1 = self.BACKEND.get_thumbnail(im, '50') th2 = self.BACKEND.get_thumbnail(im, 'x50') th3 = self.BACKEND.get_thumbnail(im, '20x20') self.assertEqual( set([th1.key, th2.key, th3.key]), set(self.KVSTORE._get(im.key, identity='thumbnails')) ) self.KVSTORE.delete_thumbnails(im) self.assertEqual( None, self.KVSTORE._get(im.key, identity='thumbnails') )
def create_thumbnail(field_name, pk, app_label, model_name, geometry_string, **options): # Note that thumbnail options must be same for a type of thumbnail. # Otherwise, different thumbnails are created. model_type = ContentType.objects.get(app_label=app_label, model=model_name) obj = model_type.get_object_for_this_type(pk=pk) image = getattr(obj, field_name) source = ImageFile(image) for key, value in default.backend.default_options.items(): options.setdefault(key, value) name = default.backend._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) source_image = default.engine.get_image(source) default.backend._create_thumbnail(source_image, geometry_string, options, thumbnail) # Need to set size to store in kvstore. size = default.engine.get_image_size(source_image) source.set_size(size) default.kvstore.get_or_set(source) default.kvstore.set(thumbnail, source)
def test_delete(self): im1 = Item.objects.get(image='100x100.jpg').image im2 = Item.objects.get(image='500x500.jpg').image default.kvstore.get_or_set(ImageFile(im1)) # exists in kvstore and in storage self.assertTrue(bool(default.kvstore.get(ImageFile(im1)))) self.assertTrue(ImageFile(im1).exists()) # delete delete(im1) self.assertFalse(bool(default.kvstore.get(ImageFile(im1)))) self.assertFalse(ImageFile(im1).exists()) default.kvstore.get_or_set(ImageFile(im2)) # exists in kvstore and in storage self.assertTrue(bool(default.kvstore.get(ImageFile(im2)))) self.assertTrue(ImageFile(im2).exists()) # delete delete(im2, delete_file=False) self.assertFalse(bool(default.kvstore.get(ImageFile(im2)))) self.assertTrue(ImageFile(im2).exists())
def twitter_card(self, instance): source = ImageFile(instance.twitter_card_image) path = thumb_storage.backend._get_thumbnail_filename(source, "300x200", {'format': 'PNG'}) full_path = os.path.join(settings.MEDIA_ROOT, path) if os.path.exists(full_path): os.unlink(full_path) thumbnail = get_thumbnail(instance.twitter_card_image, "300x200", format="PNG") settings.THUMBNAIL_FORCE_OVERWRITE = False html = """<a href="{url}" target="_blank"><img src="{img}"></a>""".format( url=instance.twitter_card_image.url, img=thumbnail.url ) return mark_safe(html)
def background_margin(file_, geometry_string): """ Returns the calculated margin for a background image and geometry """ if not file_ or sorl_settings.THUMBNAIL_DUMMY: return 'auto' margin = [0, 0] image_file = default.kvstore.get_or_set(ImageFile(file_)) x, y = parse_geometry(geometry_string, image_file.ratio) ex = x - image_file.x margin[0] = ex / 2 ey = y - image_file.y margin[1] = ey / 2 return ' '.join(['%spx' % n for n in margin])
def get_thumbnail(self, file_, geometry_string, **options): """ Returns thumbnail as an ImageFile instance for file with geometry and options given. First it will try to get it from the key value store, secondly it will create it. """ source = ImageFile(file_) for key, value in self.default_options.iteritems(): options.setdefault(key, value) # For the future I think it is better to add options only if they # differ from the default settings as below. This will ensure the same # filenames beeing generated for new options at default. for key, attr in self.extra_options: value = getattr(settings, attr) if value != getattr(default_settings, attr): options.setdefault(key, value) name = self._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) cached = default.kvstore.get(thumbnail) if cached: return cached if not thumbnail.exists(): # We have to check exists() because the Storage backend does not # overwrite in some implementations. # so we make the assumption that if the thumbnail is not cached, it doesn't exist try: source_image = default.engine.get_image(source) except IOError: # if S3Storage says file doesn't exist remotely, don't try to # create it, exit early # Will return working empty image type; 404'd image logger.warn('Remote file [%s] at [%s] does not exist', file_, geometry_string) return thumbnail # We might as well set the size since we have the image in memory image_info = default.engine.get_image_info(source_image) options['image_info'] = image_info size = default.engine.get_image_size(source_image) source.set_size(size) self._create_thumbnail(source_image, geometry_string, options, thumbnail) # If the thumbnail exists we don't create it, the other option is # to delete and write but this could lead to race conditions so I # will just leave that out for now. default.kvstore.get_or_set(source) default.kvstore.set(thumbnail, source) return thumbnail
def get_thumbnail(self, file_, geometry_string, force_create=True, **options): """ Returns thumbnail as an ImageFile instance for file with geometry and options given. First it will try to get it from the key value store, secondly it will create it. """ source = ImageFile(file_) if settings.THUMBNAIL_PRESERVE_FORMAT: format = self._get_format(file_) if format == 'GIF': options.setdefault('format', format) for key, value in self.default_options.iteritems(): options.setdefault(key, value) # For the future I think it is better to add options only if they # differ from the default settings as below. This will ensure the same # filenames beeing generated for new options at default. for key, attr in self.extra_options: value = getattr(settings, attr) if value != getattr(default_settings, attr): options.setdefault(key, value) name = self._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) cached = default.kvstore.get(thumbnail) if cached: return cached if not force_create: return source if not thumbnail.exists(): # We have to check exists() because the Storage backend does not # overwrite in some implementations. source_image = default.engine.get_image(source) # We might as well set the size since we have the image in memory size = default.engine.get_image_size(source_image) source.set_size(size) self._create_thumbnail(source_image, geometry_string, options, thumbnail) # If the thumbnail exists we don't create it, the other option is # to delete and write but this could lead to race conditions so I # will just leave that out for now. default.kvstore.get_or_set(source) default.kvstore.set(thumbnail, source) return thumbnail
def create_thumbnail(file_, geometry_string, options, name, force=False): if file_: source = ImageFile(file_) else: return thumbnail = ImageFile(name, default.storage) # We have to check exists() because the Storage backend does not # overwrite in some implementations. if settings.THUMBNAIL_FORCE_OVERWRITE or not thumbnail.exists() or force: try: source_image = default.engine.get_image(source) except IOError as e: logger.exception(e) # if S3Storage says file doesn't exist remotely, don't try to # create it and exit early. # Will return working empty image type; 404'd image logger.warn( "Remote file [{}}] at [{}}] does not exist".format(file_, geometry_string) ) return # We might as well set the size since we have the image in memory try: image_info = default.engine.get_image_info(source_image) options['image_info'] = image_info except AttributeError: options['image_info'] = {} size = default.engine.get_image_size(source_image) source.set_size(size) try: default.backend._create_thumbnail( source_image, geometry_string, options, thumbnail) default.backend._create_alternative_resolutions( source_image, geometry_string, options, thumbnail.name) finally: default.engine.cleanup(source_image) # If the thumbnail exists we don't create it, the other option is # to delete and write but this could lead to race conditions so I # will just leave that out for now. default.kvstore.get_or_set(source) default.kvstore.set(thumbnail, source)
def test_image_from_hash(self): storage = S3Boto3Storage(bucket=self.bucket) s3_file = S3Boto3StorageFile(name=self.s3_image_key, mode='r', storage=storage) # Mock model Image field # S3Boto3StorageFile store storage information in ._storage wich is not checked by # ImageFile during storage identification s3_file.storage = storage image_s3 = ImageFile(s3_file) # Test local part options = {} thumbnail = get_thumbnail(s3_file, self.geometry_string, **options) print('Generated thumbnail url: {}'.format(thumbnail.url)) print('Thumbnail cache key: {}'.format(thumbnail.name)) # Now, test shrinkmeister server (should be up and running) resp = requests.get(thumbnail.url) image = Image(blob=resp.content) self.assertEqual(image.width, 50) self.assertEqual(image.height, 38) thumbnail_from_cache = self.cache.get(thumbnail.name) self.assertNotEqual(thumbnail_from_cache, None, msg="No image in cache detected :(") image = Image(blob=resp.content) self.assertEqual(image.width, 50) self.assertEqual(image.height, 38) resp = requests.get(thumbnail_from_cache.url) url, ext = thumbnail_from_cache.url.rsplit('.', 1) x2_url = '{}@2x.{}'.format(url, ext) print('x2 url {}'.format(x2_url)) resp = requests.get(x2_url) image = Image(blob=resp.content) self.assertEqual(image.width, 100) self.assertEqual(image.height, 75)
def test_cleanup2(self): self.kvstore.clear() im = ImageFile(Item.objects.get(image='500x500.jpg').image) th3 = self.backend.get_thumbnail(im, '27x27') th4 = self.backend.get_thumbnail(im, '81x81') def keys_test(x, y, z): self.assertEqual(x, len(list(self.kvstore._find_keys(identity='image')))) self.assertEqual(y, len(list(self.kvstore._find_keys(identity='thumbnails')))) self.assertEqual(z, len(self.kvstore._get(im.key, identity='thumbnails') or [])) keys_test(3, 1, 2) th3.delete() keys_test(3, 1, 2) self.kvstore.cleanup() keys_test(2, 1, 1) th4.delete() keys_test(2, 1, 1) self.kvstore.cleanup() keys_test(1, 0, 0) self.kvstore.clear() keys_test(0, 0, 0)
def margin(file_, geometry_string): """ Returns the calculated margin for an image and geometry """ if not file_ or settings.THUMBNAIL_DUMMY: return 'auto' margin = [0, 0, 0, 0] image_file = default.kvstore.get_or_set(ImageFile(file_)) x, y = parse_geometry(geometry_string, image_file.ratio) ex = x - image_file.x margin[3] = ex / 2 margin[1] = ex / 2 if ex % 2: margin[1] += 1 ey = y - image_file.y margin[0] = ey / 2 margin[2] = ey / 2 if ey % 2: margin[2] += 1 return ' '.join(['%spx' % n for n in margin])
def test_storage_serialize(self): im = ImageFile(Item.objects.get(image='500x500.jpg').image) self.assertEqual(im.serialize_storage(), 'tests.thumbnail_tests.storage.TestStorage') self.assertEqual( ImageFile('http://www.image.jpg').serialize_storage(), 'sorl.thumbnail.images.UrlStorage', ) self.assertEqual( ImageFile('http://www.image.jpg', default.storage).serialize_storage(), 'tests.thumbnail_tests.storage.TestStorage', ) self.assertEqual( ImageFile('getit', default_storage).serialize_storage(), 'tests.thumbnail_tests.storage.TestStorage', )
def get_thumbnail(self, file_, geometry_string, **options): source = ImageFile(file_) for key, value in self.default_options.items(): options.setdefault(key, value) name = self._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) cached = default.kvstore.get(thumbnail) if cached: return cached # We don't check if thumbnail exists as sorl-thumbnail does. It becomes # very costly for remote storages. # Furthermore, I have added following code to reduce/prevent duplicate # tasks in celery. It's hacky. # Finally, if there is no thumbnail, we create one. from .tasks import create_thumbnail job = create_thumbnail.delay(source.name, geometry_string, **options) # Sometimes thumbnail generation takes quite some time, just return # the original image then. if job: geometry = parse_geometry(geometry_string) # We can't add a source row to the kvstore without the size # information being looked up, so add dummy information here # We'll need to correct this information when we generate the thumbnail source.set_size(geometry) default.kvstore.get_or_set(source) # We don't want to do any file access in this thread, so we tell sorlery # to proceed as normal and cheekily update the name and storage after # the hash has been calculated. thumbnail.set_size(geometry) default.kvstore.set(thumbnail, source) # Now we go back and manually update the thumbnail to point at the source image # Hopefully someone can suggest a better way to do this ... but the sorl internals # don't make it easy to. rawvalue = default.kvstore._get_raw(add_prefix(thumbnail.key)) rawvaluearr = deserialize(rawvalue) rawvaluearr['name'] = file_.name default.kvstore._set_raw(add_prefix(thumbnail.key), serialize(rawvaluearr)) thumbnail.name = file_.name return thumbnail
def test_relative_absolute_same_key(self): image = Item.objects.get(image='500x500.jpg').image imref1 = ImageFile(image.name) imref2 = ImageFile(os.path.join(settings.MEDIA_ROOT, image.name)) self.assertEqual(imref1.key, imref2.key) self.create_image('medialibrary.jpg', (100, 100)) image = Item.objects.get(image='medialibrary.jpg').image imref1 = ImageFile(image.name) imref2 = ImageFile(os.path.join(settings.MEDIA_ROOT, image.name)) self.assertEqual(imref1.key, imref2.key) self.create_image('mediaäöü.jpg', (100, 100)) image = Item.objects.get(image='mediaäöü.jpg').image imref1 = ImageFile(image.name) imref2 = ImageFile(os.path.join(settings.MEDIA_ROOT, image.name)) self.assertEqual(imref1.key, imref2.key)
def sorl_margin(file_, geometry_string): """ This is copied from sorl/thumbnail/templatetags/thumbnail.py because of problems with importing it. This should be removed when we remove easy_thumbnail. Returns the calculated margin for an image and geometry """ if not file_ or settings.THUMBNAIL_DUMMY: return 'auto' margin = [0, 0, 0, 0] image_file = default.kvstore.get_or_set(ImageFile(file_)) x, y = parse_geometry(geometry_string, image_file.ratio) ex = x - image_file.x margin[3] = ex / 2 margin[1] = ex / 2 if ex % 2: margin[1] += 1 ey = y - image_file.y margin[0] = ey / 2 margin[2] = ey / 2 if ey % 2: margin[2] += 1 return ' '.join(['%spx' % n for n in margin])
def test_is_portrait(self): im = ImageFile(Item.objects.get(image='500x500.jpg').image) th = self.BACKEND.get_thumbnail(im, '50x200', crop='center') self.assertEqual(th.is_portrait(), True) th = self.BACKEND.get_thumbnail(im, '500x2', crop='center') self.assertEqual(th.is_portrait(), False)
def get_thumbnail_custom(self, file_, geometry_string, **options): """ Returns thumbnail as an ImageFile instance for file with geometry and options given. First it will try to get it from the key value store, secondly it will create it. """ from sorl.thumbnail import delete dummy_source = settings.THUMBNAIL_DUMMY_SOURCE if 'dummy_source_size' in options: if options['dummy_source_size'] == 'small': dummy_source = settings.THUMBNAIL_DUMMY_SOURCE_SMALL if file_: source = ImageFile(file_) elif settings.THUMBNAIL_DUMMY: source = ImageFile(dummy_source) #return DummyImageFile(geometry_string) else: return None # preserve image filetype if settings.THUMBNAIL_PRESERVE_FORMAT: options.setdefault('format', self._get_format(source)) for key, value in self.default_options.items(): options.setdefault(key, value) # For the future I think it is better to add options only if they # differ from the default settings as below. This will ensure the same # filenames being generated for new options at default. for key, attr in self.extra_options: value = getattr(settings, attr) if value != getattr(default_settings, attr): options.setdefault(key, value) cache_name = self._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(cache_name, default.storage) cached = default.kvstore.get(thumbnail) if cached: if not thumbnail.exists(): # CHECK IF CACHED FILE WAS DELETED MANUAL delete(cached) return self.get_thumbnail_custom(file_, geometry_string, **options) return cached media_folder = settings.MEDIA_URL # We have to check exists() because the Storage backend does not # overwrite in some implementations. if not self.check_exists(thumbnail): # TRY WITH SORL default.engine if source.name.startswith(media_folder): source.name = source.name.replace(media_folder, '') try: source_image = default.engine.get_image(source) except (IOError, UnicodeEncodeError) as error: if type(error) == UnicodeEncodeError: thumbnail = self.create_translit_cache( source, geometry_string, options, cache_name) if thumbnail: return thumbnail #print "GET_THUMBNAIL[1]: %s, URL= '%s', CREATING CACHE FILE" % (error, source.url) #return self.create_dummy_cache(source, cache_name, media_folder, dummy_source) options[ 'level'] = options['level'] if 'level' in options else 1 if options['level'] == 1: options['level'] += 1 return self.get_thumbnail_custom(dummy_source, geometry_string, **options) else: return DummyImageFile(geometry_string) # We might as well set the size since we have the image in memory image_info = default.engine.get_image_info(source_image) options['image_info'] = image_info size = default.engine.get_image_size(source_image) source.set_size(size) try: self._create_thumbnail(source_image, geometry_string, options, thumbnail) self._create_alternative_resolutions(source_image, geometry_string, options, thumbnail.name) except SystemError as error: # when image transparent, can't create thumbnail for it print(error) # return DummyImageFile(geometry_string) finally: default.engine.cleanup(source_image) # If the thumbnail exists we don't create it, the other option is # to delete and write but this could lead to race conditions so I # will just leave that out for now. try: default.kvstore.get_or_set(source) except IOError as error: #print "GET_THUMBNAIL[2]: %s, URL= '%s'. CREATING CACHE FILE" % (error, cache_name) #return self.create_dummy_cache(source, cache_name, media_folder, dummy_source) #return self.get_thumbnail_custom(dummy_source, geometry_string, **options) return DummyImageFile(geometry_string) try: default.kvstore.set(thumbnail, source) except IOError as error: #print "GET_THUMBNAIL[3]: %s, URL= '%s'. CREATING CACHE FILE" % (error, cache_name) #return self.create_dummy_cache(source, cache_name, media_folder, dummy_source) #return self.get_thumbnail_custom(dummy_source, geometry_string, **options) return DummyImageFile(geometry_string) return thumbnail
def test_write(self): with same_open_fd_count(self): with self.assertRaises(Exception): self.ENGINE.write(image=self.ENGINE.get_image(StringIO(b'xxx')), options={'format': 'JPEG', 'quality': 90, 'image_info': {}}, thumbnail=ImageFile('whatever_thumb.jpg', default.storage))
def get_thumbnail(self, file_, geometry_string, **options): """ Returns thumbnail as an ImageFile instance for file with geometry and options given. First it will try to get it from the key value store, secondly it will create it. """ logger.debug('Getting thumbnail for file [%s] at [%s]', file_, geometry_string) if file_: source = ImageFile(file_) else: raise ValueError('falsey file_ argument in get_thumbnail()') # preserve image filetype if settings.THUMBNAIL_PRESERVE_FORMAT: options.setdefault('format', self._get_format(source)) for key, value in self.default_options.items(): options.setdefault(key, value) # For the future I think it is better to add options only if they # differ from the default settings as below. This will ensure the same # filenames being generated for new options at default. for key, attr in self.extra_options: value = getattr(settings, attr) if value != getattr(default_settings, attr): options.setdefault(key, value) name = self._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) cached = default.kvstore.get(thumbnail) if cached: return cached # We have to check exists() because the Storage backend does not # overwrite in some implementations. if settings.THUMBNAIL_FORCE_OVERWRITE or not thumbnail.exists(): try: source_image = default.engine.get_image(source) except IOError as e: logger.exception(e) if settings.THUMBNAIL_DUMMY: return DummyImageFile(geometry_string) else: # if S3Storage says file doesn't exist remotely, don't try to # create it and exit early. # Will return working empty image type; 404'd image logger.warning( 'Remote file [%s] at [%s] does not exist', file_, geometry_string, ) return thumbnail # We might as well set the size since we have the image in memory image_info = default.engine.get_image_info(source_image) options['image_info'] = image_info size = default.engine.get_image_size(source_image) source.set_size(size) try: self._create_thumbnail(source_image, geometry_string, options, thumbnail) self._create_alternative_resolutions(source_image, geometry_string, options, thumbnail.name) finally: default.engine.cleanup(source_image) # If the thumbnail exists we don't create it, the other option is # to delete and write but this could lead to race conditions so I # will just leave that out for now. default.kvstore.get_or_set(source) default.kvstore.set(thumbnail, source) return thumbnail
def get_thumbnail(self, file_, geometry_string, **options): """ Returns thumbnail as an ImageFile instance for file with geometry and options given. First it will try to get it from the key value store, secondly it will create it. """ logger.debug(text_type('Getting thumbnail for file [%s] at [%s]'), file_, geometry_string) async = options.pop('async', True) if not async: return super(QueuedThumbnailBackend, self).get_thumbnail(file_, geometry_string, **options) if file_: source = ImageFile(file_) elif settings.THUMBNAIL_DUMMY: return DummyImageFile(geometry_string) else: return None # preserve image filetype if settings.THUMBNAIL_PRESERVE_FORMAT: options.setdefault('format', self._get_format(source)) for key, value in self.default_options.items(): options.setdefault(key, value) # For the future I think it is better to add options only if they # differ from the default settings as below. This will ensure the same # filenames being generated for new options at default. for key, attr in self.extra_options: value = getattr(settings, attr) if value != getattr(default_settings, attr): options.setdefault(key, value) name = self._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) cached = default.kvstore.get(thumbnail) if cached: return cached # We cannot check if the file exists, as remote storage is slow. If # we have reached this point, the image does not exist in our kvstore # so create the entry and queue the generation of the image. # # Note: If the thumbnail file has been deleted, you will need to manually # clear the corresponding row from the kvstore to have thumbnail rebuilt. job = create_thumbnail.delay(file_, geometry_string, options, name) if isinstance(file_, string_type): filename = file_.split('/')[-1] else: filename = file_.name if job: geometry = (0, 0) # We can't add a source row to the kvstore without the size # information being looked up, so add dummy information here # We'll need to correct this information when we generate the thumbnail source.set_size(geometry) default.kvstore.get_or_set(source) # We don't want to do any file access in this thread, so we tell sorlery # to proceed as normal and cheekily update the name and storage after # the hash has been calculated. thumbnail.set_size(geometry) default.kvstore.set(thumbnail, source) # Now we go back and manually update the thumbnail to point at the source image # Hopefully someone can suggest a better way to do this ... but the sorl internals # don't make it easy to. rawvalue = default.kvstore._get_raw(add_prefix(thumbnail.key)) rawvaluearr = deserialize(rawvalue) rawvaluearr['name'] = filename default.kvstore._set_raw(add_prefix(thumbnail.key), serialize(rawvaluearr)) thumbnail.name = filename return thumbnail
def get_thumbnail(self, file_, geometry_string, **options): # Correct way for create source image, This is coped from begining of # ThumbnailBackend.get_thumbnail if file_: source = ImageFile(file_) elif settings.THUMBNAIL_DUMMY: return DummyImageFile(geometry_string) else: return None for key, value in self.default_options.iteritems(): options.setdefault(key, value) for key, attr in self.extra_options: value = getattr(settings, attr) if value != getattr(default_settings, attr): options.setdefault(key, value) # Generate a name for the thumbnail name = self._get_thumbnail_filename(source, geometry_string, options) # See if we've got a hit in the cache thumbnail = ImageFile(name, default.storage) cached = default.kvstore.get(thumbnail) if cached: return cached #fixing misssing 'image_info' to be compatible with sorl-thumbnail newewst version else: # We have to check exists() because the Storage backend does not # overwrite in some implementations. # so we make the assumption that if the thumbnail is not cached, it doesn't exist try: source_image = default.engine.get_image(source) except IOError: if settings.THUMBNAIL_DUMMY: return DummyImageFile(geometry_string) else: # if S3Storage says file doesn't exist remotely, don't try to # create it and exit early. # Will return working empty image type; 404'd image logger.warn('Remote file [%s] at [%s] does not exist', file_, geometry_string) return thumbnail # We might as well set the size since we have the image in memory image_info = default.engine.get_image_info(source_image) options['image_info'] = image_info # We cannot check if the file exists, as remote storage is slow. If # we have reached this point, the image does not exist in our kvstore # so create the entry and queue the generation of the image. # # Note: If the thumbnail file has been deleted, you will need to manually # clear the corresponding row from the kvstore to have thumbnail rebuilt. job = create_thumbnail.delay(file_, geometry_string, options, name) if job: geometry = parse_geometry(geometry_string) # We can't add a source row to the kvstore without the size # information being looked up, so add dummy information here # We'll need to correct this information when we generate the thumbnail source.set_size(geometry) default.kvstore.get_or_set(source) # We don't want to do any file access in this thread, so we tell sorlery # to proceed as normal and cheekily update the name and storage after # the hash has been calculated. thumbnail.set_size(geometry) default.kvstore.set(thumbnail, source) # Now we go back and manually update the thumbnail to point at the source image # Hopefully someone can suggest a better way to do this ... but the sorl internals # don't make it easy to. rawvalue = default.kvstore._get_raw(add_prefix(thumbnail.key)) rawvaluearr = deserialize(rawvalue) rawvaluearr['name'] = source.name default.kvstore._set_raw(add_prefix(thumbnail.key), serialize(rawvaluearr)) thumbnail.name = source.name return thumbnail
def testKVStoreGetSet(self): im = ImageFile(Item.objects.get(image='500x500.jpg').image) self.kvstore.delete(im) self.assertEqual(self.kvstore.get(im), None) self.kvstore.set(im) self.assertEqual(im.size, [500, 500])
def setUp(self): name = 'org.jpg' os.makedirs(settings.MEDIA_ROOT) fn = pjoin(settings.MEDIA_ROOT, name) Image.new('L', (100, 100)).save(fn) self.im = ImageFile(name)
def get_thumbnail(self, file_, geometry_string, **options): """ Returns thumbnail as an ImageFile instance for file with geometry and options given. First it will try to get it from the key value store, secondly it will create it. """ logger.debug(text_type('Getting thumbnail for file [%s] at [%s]'), file_, geometry_string) async = options.pop('async', True) if not async: return super(QueuedThumbnailBackend, self).get_thumbnail( file_, geometry_string, **options) if file_: source = ImageFile(file_) elif settings.THUMBNAIL_DUMMY: return DummyImageFile(geometry_string) else: return None # preserve image filetype if settings.THUMBNAIL_PRESERVE_FORMAT: options.setdefault('format', self._get_format(source)) for key, value in self.default_options.items(): options.setdefault(key, value) # For the future I think it is better to add options only if they # differ from the default settings as below. This will ensure the same # filenames being generated for new options at default. for key, attr in self.extra_options: value = getattr(settings, attr) if value != getattr(default_settings, attr): options.setdefault(key, value) name = self._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) cached = default.kvstore.get(thumbnail) if cached: return cached # We cannot check if the file exists, as remote storage is slow. If # we have reached this point, the image does not exist in our kvstore # so create the entry and queue the generation of the image. # # Note: If the thumbnail file has been deleted, you will need to manually # clear the corresponding row from the kvstore to have thumbnail rebuilt. job = create_thumbnail.delay(file_, geometry_string, options, name) if isinstance(file_, string_type): filename = file_.split('/')[-1] else: filename = file_.name if job: geometry = (0, 0) # We can't add a source row to the kvstore without the size # information being looked up, so add dummy information here # We'll need to correct this information when we generate the thumbnail source.set_size(geometry) default.kvstore.get_or_set(source) # We don't want to do any file access in this thread, so we tell sorlery # to proceed as normal and cheekily update the name and storage after # the hash has been calculated. thumbnail.set_size(geometry) default.kvstore.set(thumbnail, source) # Now we go back and manually update the thumbnail to point at the source image # Hopefully someone can suggest a better way to do this ... but the sorl internals # don't make it easy to. rawvalue = default.kvstore._get_raw(add_prefix(thumbnail.key)) rawvaluearr = deserialize(rawvalue) rawvaluearr['name'] = filename default.kvstore._set_raw(add_prefix(thumbnail.key), serialize(rawvaluearr)) thumbnail.name = filename return thumbnail
def test_kvstore_get_and_set(self): im = ImageFile(Item.objects.get(image='500x500.jpg').image) self.KVSTORE.delete(im) self.assertEqual(self.KVSTORE.get(im), None) self.KVSTORE.set(im) self.assertEqual(im.size, [500, 500])
def get_thumbnail(self, file_, geometry_string, **options): """ Return thumbnails as base64 url if not exists in cache and delay convert process. Modified version of original get_thumbnail method. """ if not options.pop('lazy', False): return super(LazyThumbnailBackend, self).get_thumbnail(file_, geometry_string, **options) logger.debug(text_type('Getting thumbnail for file [%s] at [%s]'), file_, geometry_string) if file_: source = ImageFile(file_) elif settings.THUMBNAIL_DUMMY: return DummyImageFile(geometry_string) else: return None # preserve image filetype if settings.THUMBNAIL_PRESERVE_FORMAT: options.setdefault('format', self._get_format(source)) for key, value in self.default_options.items(): options.setdefault(key, value) # For the future I think it is better to add options only if they # differ from the default settings as below. This will ensure the same # filenames being generated for new options at default. for key, attr in self.extra_options: value = getattr(settings, attr) if value != getattr(default_settings, attr): options.setdefault(key, value) name = self._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) cached = default.kvstore.get(thumbnail) if cached: return cached # MODIFIED CODE. Overwriting storage for lazy base64 thumbnail = ImageFile(name, lazy_storage) generate_thumbnail_lazy.delay(file_, geometry_string, **options) # End of MODIFIED CODE try: source_image = default.engine.get_image(source) except IOError as e: logger.exception(e) if settings.THUMBNAIL_DUMMY: return DummyImageFile(geometry_string) else: # if S3Storage says file doesn't exist remotely, don't try to # create it and exit early. # Will return working empty image type; 404'd image logger.warn(text_type('Remote file [%s] at [%s] does not exist'), file_, geometry_string) return thumbnail # We might as well set the size since we have the image in memory image_info = default.engine.get_image_info(source_image) options['image_info'] = image_info size = default.engine.get_image_size(source_image) source.set_size(size) try: self._create_thumbnail(source_image, geometry_string, options, thumbnail) finally: default.engine.cleanup(source_image) return thumbnail
def test_relative_absolute_same_key(self): image = Item.objects.get(image='500x500.jpg').image imref1 = ImageFile(image.name) imref2 = ImageFile(pjoin(settings.MEDIA_ROOT, image.name)) self.assertEqual(imref1.key, imref2.key)
def get_thumbnail(self, file_, geometry_string, **options): """ Returns thumbnail as an ImageFile instance for file with geometry and options given. First it will try to get it from the key value store, secondly it will create it. """ logger.debug('Getting thumbnail for file [%s] at [%s]', file_, geometry_string) if file_: source = ImageFile(file_) elif settings.THUMBNAIL_DUMMY: return DummyImageFile(geometry_string) else: return None # preserve image filetype if settings.THUMBNAIL_PRESERVE_FORMAT: options.setdefault('format', self._get_format(source)) for key, value in self.default_options.items(): options.setdefault(key, value) # For the future I think it is better to add options only if they # differ from the default settings as below. This will ensure the same # filenames being generated for new options at default. for key, attr in self.extra_options: value = getattr(settings, attr) if value != getattr(default_settings, attr): options.setdefault(key, value) name = self._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) cached = default.kvstore.get(thumbnail) if cached: return cached # We have to check exists() because the Storage backend does not # overwrite in some implementations. if settings.THUMBNAIL_FORCE_OVERWRITE or not thumbnail.exists(): try: source_image = default.engine.get_image(source) except IOError as e: logger.exception(e) if settings.THUMBNAIL_DUMMY: return DummyImageFile(geometry_string) else: # if S3Storage says file doesn't exist remotely, don't try to # create it and exit early. # Will return working empty image type; 404'd image logger.warning( 'Remote file [%s] at [%s] does not exist', file_, geometry_string, ) return thumbnail # We might as well set the size since we have the image in memory image_info = default.engine.get_image_info(source_image) options['image_info'] = image_info size = default.engine.get_image_size(source_image) source.set_size(size) try: self._create_thumbnail(source_image, geometry_string, options, thumbnail) self._create_alternative_resolutions(source_image, geometry_string, options, thumbnail.name) finally: default.engine.cleanup(source_image) # If the thumbnail exists we don't create it, the other option is # to delete and write but this could lead to race conditions so I # will just leave that out for now. default.kvstore.get_or_set(source) default.kvstore.set(thumbnail, source) return thumbnail
def static_image(url): storage_class = get_storage_class(settings.STATICFILES_STORAGE) storage = storage_class() image = ImageFile(storage.open(url)) image.storage = storage return image, image.url