mirror of
https://github.com/Hopiu/django-imagekit.git
synced 2026-03-17 05:40:25 +00:00
This extra layer of indirection allows us to tack some attributes (name and content_type) onto the underlying file, which we cannot do with a StringIO since it's a native ctype. These attributes are used by various third-party software that expects to work with django.core.files.File instances, and not directly with StringIO instances. By way of example, the django-storages mosso backend (CloudFilesStorage) looks for a content_type attribute, and the cloudfiles Object backend looks for a name attribute.
119 lines
4 KiB
Python
119 lines
4 KiB
Python
import mimetypes
|
|
import os
|
|
|
|
from StringIO import StringIO
|
|
|
|
from django.core.files.base import ContentFile
|
|
|
|
from .processors import ProcessorPipeline, AutoConvert
|
|
from .utils import img_to_fobj, open_image, \
|
|
format_to_extension, extension_to_format, UnknownFormatError, \
|
|
UnknownExtensionError
|
|
|
|
|
|
class SpecFile(ContentFile):
|
|
"""
|
|
Wraps a ContentFile in a file-like object with a filename
|
|
and a content_type.
|
|
"""
|
|
def __init__(self, filename, content):
|
|
self.file = ContentFile(content)
|
|
self.file.name = filename
|
|
try:
|
|
self.file.content_type = mimetypes.guess_type(filename)[0]
|
|
except IndexError:
|
|
self.file.content_type = None
|
|
|
|
def __str__(self):
|
|
return self.file.name
|
|
|
|
|
|
class SpecFileGenerator(object):
|
|
def __init__(self, processors=None, format=None, options={},
|
|
autoconvert=True, storage=None):
|
|
self.processors = processors
|
|
self.format = format
|
|
self.options = options
|
|
self.autoconvert = autoconvert
|
|
self.storage = storage
|
|
|
|
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)
|
|
img = ProcessorPipeline(processors or []).process(img)
|
|
|
|
options = dict(self.options or {})
|
|
|
|
# Determine the format.
|
|
format = self.format
|
|
if not format:
|
|
# Try to guess the format from the extension.
|
|
extension = os.path.splitext(filename)[1].lower()
|
|
if extension:
|
|
try:
|
|
format = extension_to_format(extension)
|
|
except UnknownExtensionError:
|
|
pass
|
|
format = format or img.format or original_format or 'JPEG'
|
|
|
|
# Run the AutoConvert processor
|
|
if self.autoconvert:
|
|
autoconvert_processor = AutoConvert(format)
|
|
img = autoconvert_processor.process(img)
|
|
options = dict(autoconvert_processor.save_kwargs.items() + \
|
|
options.items())
|
|
|
|
imgfile = img_to_fobj(img, format, **options)
|
|
content = SpecFile(filename, imgfile.read())
|
|
return img, content
|
|
|
|
def suggest_extension(self, name):
|
|
original_extension = os.path.splitext(name)[1]
|
|
try:
|
|
suggested_extension = format_to_extension(self.format)
|
|
except UnknownFormatError:
|
|
extension = original_extension
|
|
else:
|
|
if suggested_extension.lower() == original_extension.lower():
|
|
extension = original_extension
|
|
else:
|
|
try:
|
|
original_format = extension_to_format(original_extension)
|
|
except UnknownExtensionError:
|
|
extension = suggested_extension
|
|
else:
|
|
# If the formats match, give precedence to the original extension.
|
|
if self.format.lower() == original_format.lower():
|
|
extension = original_extension
|
|
else:
|
|
extension = suggested_extension
|
|
return extension
|
|
|
|
def generate_file(self, filename, source_file, save=True):
|
|
"""
|
|
Generates a new image file by processing the source file and returns
|
|
the content of the result, ready for saving.
|
|
|
|
"""
|
|
if source_file: # TODO: Should we error here or something if the source_file doesn't exist?
|
|
# Process the original image file.
|
|
|
|
try:
|
|
fp = source_file.storage.open(source_file.name)
|
|
except IOError:
|
|
return
|
|
fp.seek(0)
|
|
fp = StringIO(fp.read())
|
|
|
|
img, content = self.process_content(fp, filename, source_file)
|
|
|
|
if save:
|
|
storage = self.storage or source_file.storage
|
|
storage.save(filename, content)
|
|
|
|
return content
|