mirror of
https://github.com/Hopiu/django-markdownx.git
synced 2026-03-16 21:40:24 +00:00
Rectified the error causing image size alteration for non-svg images being ignored. More clean ups, and breakdown of functions inline with functional/OOP best practices, to make them easier to debug.
This commit is contained in:
parent
b76d0a1d90
commit
9d6cb94bd8
1 changed files with 87 additions and 47 deletions
|
|
@ -1,14 +1,17 @@
|
|||
from os import path
|
||||
from os import SEEK_END
|
||||
# Python internal library.
|
||||
from os import path, SEEK_END
|
||||
from uuid import uuid4
|
||||
from collections import namedtuple
|
||||
|
||||
# Django library.
|
||||
from django import forms
|
||||
from django.utils.six import BytesIO
|
||||
from django.core.files.storage import default_storage
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.files.uploadedfile import InMemoryUploadedFile
|
||||
from django.template import defaultfilters as filters
|
||||
from django.template.defaultfilters import filesizeformat
|
||||
|
||||
# Internal.
|
||||
from .utils import scale_and_crop
|
||||
from .settings import (
|
||||
MARKDOWNX_IMAGE_MAX_SIZE,
|
||||
|
|
@ -17,20 +20,33 @@ from .settings import (
|
|||
MARKDOWNX_UPLOAD_MAX_SIZE,
|
||||
)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# Constants
|
||||
# -------------------------------------------------------
|
||||
|
||||
SVG_TYPE = 'image/svg+xml'
|
||||
SVG_EXTENSION = 'svg'
|
||||
|
||||
# -------------------------------------------------------
|
||||
|
||||
|
||||
class ImageForm(forms.Form):
|
||||
|
||||
image = forms.FileField()
|
||||
|
||||
# Separately defined as it needs to be processed a text file rather than image.
|
||||
_SVG_TYPE = 'image/svg+xml'
|
||||
|
||||
_error_templates = {
|
||||
# No file is available to upload.
|
||||
'not_uploaded':
|
||||
forms.ValidationError(_('No files have been uploaded.')),
|
||||
|
||||
# The file is of a format not defined in "settings.py"
|
||||
# or if default, in "markdownx/settings.py".
|
||||
'unsupported_format':
|
||||
forms.ValidationError(_('File type is not supported.')),
|
||||
|
||||
# The file is larger in size that the maximum allow in "settings.py" (or the default).
|
||||
'invalid_size':
|
||||
lambda current: forms.ValidationError(
|
||||
_('Please keep file size under {max}. Current file size {current}').format(
|
||||
max=filesizeformat(MARKDOWNX_UPLOAD_MAX_SIZE),
|
||||
current=filesizeformat(current)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def save(self, commit=True):
|
||||
"""
|
||||
Saves the uploaded image in the designated location.
|
||||
|
|
@ -39,80 +55,104 @@ class ImageForm(forms.Form):
|
|||
subsequently save; otherwise, the SVG is saved in its existing `charset`
|
||||
as an `image/svg+xml`.
|
||||
|
||||
The dimension of image files (excluding SVG) are set using `Pillow`.
|
||||
The dimension of image files (excluding SVG) are set using `PIL`.
|
||||
|
||||
:param commit: If `True`, the file is saved to the disk; otherwise, it is held in the memory.
|
||||
:type commit: bool
|
||||
:return: An instance of saved image if `commit` is `True`, else `full_path, uploaded_image`.
|
||||
:return: An instance of saved image if `commit is True`, else `namedtuple(path, image)`.
|
||||
:rtype: bool, namedtuple
|
||||
"""
|
||||
image = self.files.get('image')
|
||||
content_type = image.content_type
|
||||
thumb_io = None
|
||||
file_name = image.name
|
||||
image_extension = content_type.split('/')[-1].upper()
|
||||
image_size = getattr(image, '_size')
|
||||
|
||||
if image.content_type is SVG_TYPE:
|
||||
thumb_io = BytesIO()
|
||||
preped_image = scale_and_crop(image, **MARKDOWNX_IMAGE_MAX_SIZE)
|
||||
image_extension = image.content_type.split('/')[-1].upper()
|
||||
preped_image.save(thumb_io, image_extension)
|
||||
file_name = str(image)
|
||||
thumb_io.seek(0, SEEK_END)
|
||||
if content_type.lower() != self._SVG_TYPE:
|
||||
# Processing the raster graphic image:
|
||||
image = self._process_raster(image, image_extension)
|
||||
image_size = image.tell()
|
||||
|
||||
# Processed file (or the actual file in the case of SVG) is now
|
||||
# saved in the memory as a Django object.
|
||||
uploaded_image = InMemoryUploadedFile(
|
||||
file=thumb_io if thumb_io else image,
|
||||
file=image,
|
||||
field_name=None,
|
||||
name=file_name,
|
||||
content_type=content_type,
|
||||
size=thumb_io.tell() if thumb_io else getattr(image, '_size'),
|
||||
size=image_size,
|
||||
charset=None
|
||||
)
|
||||
|
||||
return self._save(uploaded_image, file_name, commit)
|
||||
|
||||
def _save(self, image, file_name, commit):
|
||||
"""
|
||||
Final saving process, called internally after the image had processed.
|
||||
"""
|
||||
# Defining a universally unique name for the file
|
||||
# to be saved on the disk.
|
||||
unique_file_name = self.get_unique_file_name(file_name)
|
||||
full_path = path.join(MARKDOWNX_MEDIA_PATH, unique_file_name)
|
||||
|
||||
if commit:
|
||||
default_storage.save(full_path, uploaded_image)
|
||||
default_storage.save(full_path, image)
|
||||
return default_storage.url(full_path)
|
||||
|
||||
# If `commit=False`, return the path and in-memory image.
|
||||
return full_path, uploaded_image
|
||||
# If `commit is False`, return the path and in-memory image.
|
||||
image_data = namedtuple('image_data', ['path', 'image'])
|
||||
return image_data(path=full_path, image=image)
|
||||
|
||||
@staticmethod
|
||||
def get_unique_file_name(filename):
|
||||
def _process_raster(image, extension):
|
||||
"""
|
||||
Generates a universally unique ID using Python `UUID` and
|
||||
attaches the extension of file name to it.
|
||||
:param filename: Name of the uploaded file, including the extension.
|
||||
:type filename: str
|
||||
:return: Universally unique ID, ending with the extension extracted from `filename`.
|
||||
Processing of raster graphic image.
|
||||
"""
|
||||
# File needs to be uploaded and saved temporarily in
|
||||
# the memory for additional processing using PIL.
|
||||
thumb_io = BytesIO()
|
||||
preped_image = scale_and_crop(image, **MARKDOWNX_IMAGE_MAX_SIZE)
|
||||
preped_image.save(thumb_io, extension)
|
||||
thumb_io.seek(0, SEEK_END)
|
||||
return thumb_io
|
||||
|
||||
@staticmethod
|
||||
def get_unique_file_name(file_name):
|
||||
"""
|
||||
Generates a universally unique ID using Python `UUID` and attaches the extension of file name to it.
|
||||
|
||||
:param file_name: Name of the uploaded file, including the extension.
|
||||
:type file_name: str
|
||||
:return: Universally unique ID, ending with the extension extracted from `file_name`.
|
||||
:rtype: str
|
||||
"""
|
||||
ext = filename.split('.')[-1]
|
||||
filename = "%s.%s" % (uuid4(), ext)
|
||||
return filename
|
||||
file_name = "{unique_name}.{extension}".format(
|
||||
unique_name=uuid4(),
|
||||
extension=path.splitext(file_name)[1][1:] # [1] is the extension, [1:] discards the dot.
|
||||
)
|
||||
return file_name
|
||||
|
||||
def clean(self):
|
||||
"""
|
||||
Checks the upload against allowed extensions and maximum size.
|
||||
|
||||
:return: Upload
|
||||
"""
|
||||
upload = self.cleaned_data.get('image')
|
||||
|
||||
# -----------------------------------------------
|
||||
# See comments in `self._error_templates` for
|
||||
# additional information on each error.
|
||||
# -----------------------------------------------
|
||||
if not upload:
|
||||
raise forms.ValidationError(_('No files have been uploaded.'))
|
||||
raise self._error_templates['not_uploaded']
|
||||
|
||||
content_type = upload.content_type
|
||||
file_size = getattr(upload, '_size')
|
||||
|
||||
if content_type not in MARKDOWNX_UPLOAD_CONTENT_TYPES:
|
||||
raise forms.ValidationError(_('File type is not supported.'))
|
||||
|
||||
raise self._error_templates['unsupported_format']
|
||||
elif file_size > MARKDOWNX_UPLOAD_MAX_SIZE:
|
||||
raise forms.ValidationError(
|
||||
_('Please keep file size under %(max)s. Current file size %(current)s') %
|
||||
{
|
||||
'max': filters.filesizeformat(MARKDOWNX_UPLOAD_MAX_SIZE),
|
||||
'current': filters.filesizeformat(upload._size)
|
||||
}
|
||||
)
|
||||
raise self._error_templates['invalid_size'](file_size)
|
||||
|
||||
return upload
|
||||
|
|
|
|||
Loading…
Reference in a new issue