mirror of
https://github.com/Hopiu/django-markdownx.git
synced 2026-03-17 05:50:23 +00:00
* Added SVG image upload functionality, cleaned up the code slightly, and added some documentations. * Added 'image/svg+xml' example to MARKDOWNX_UPLOAD_CONTENT_TYPES in settings.
118 lines
4 KiB
Python
Executable file
118 lines
4 KiB
Python
Executable file
from os import path
|
|
from os import SEEK_END
|
|
from uuid import uuid4
|
|
|
|
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 .utils import scale_and_crop
|
|
from .settings import (
|
|
MARKDOWNX_IMAGE_MAX_SIZE,
|
|
MARKDOWNX_MEDIA_PATH,
|
|
MARKDOWNX_UPLOAD_CONTENT_TYPES,
|
|
MARKDOWNX_UPLOAD_MAX_SIZE,
|
|
)
|
|
|
|
# -------------------------------------------------------
|
|
# Constants
|
|
# -------------------------------------------------------
|
|
|
|
SVG_TYPE = 'image/svg+xml'
|
|
SVG_EXTENSION = 'svg'
|
|
|
|
# -------------------------------------------------------
|
|
|
|
|
|
class ImageForm(forms.Form):
|
|
|
|
image = forms.FileField()
|
|
|
|
def save(self, commit=True):
|
|
"""
|
|
Saves the uploaded image in the designated location.
|
|
|
|
If image type is not SVG, a byteIO of image content_type is created and
|
|
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`.
|
|
|
|
: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`.
|
|
"""
|
|
image = self.files.get('image')
|
|
content_type = image.content_type
|
|
thumb_io = None
|
|
file_name = image.name
|
|
|
|
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)
|
|
|
|
uploaded_image = InMemoryUploadedFile(
|
|
file=thumb_io if thumb_io else image,
|
|
field_name=None,
|
|
name=file_name,
|
|
content_type=content_type,
|
|
size=thumb_io.tell() if thumb_io else getattr(image, '_size'),
|
|
charset=None
|
|
)
|
|
|
|
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)
|
|
return default_storage.url(full_path)
|
|
|
|
# If `commit=False`, return the path and in-memory image.
|
|
return full_path, uploaded_image
|
|
|
|
@staticmethod
|
|
def get_unique_file_name(filename):
|
|
"""
|
|
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`.
|
|
:rtype: str
|
|
"""
|
|
ext = filename.split('.')[-1]
|
|
filename = "%s.%s" % (uuid4(), ext)
|
|
return filename
|
|
|
|
def clean(self):
|
|
"""
|
|
Checks the upload against allowed extensions and maximum size.
|
|
:return: Upload
|
|
"""
|
|
upload = self.cleaned_data.get('image')
|
|
if not upload:
|
|
raise forms.ValidationError(_('No files have been 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.'))
|
|
|
|
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)
|
|
}
|
|
)
|
|
|
|
return upload
|