From de3660b23cd3db2932f80ff0614c31b6d5a35075 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Sun, 6 Sep 2020 12:04:54 +0200 Subject: [PATCH 1/2] Move image upload code to form clean --- apps/member/forms.py | 36 ++++++++++++++++++++++++++++++++++++ apps/member/views.py | 24 +++--------------------- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/apps/member/forms.py b/apps/member/forms.py index a5d571b6..abefdf2c 100644 --- a/apps/member/forms.py +++ b/apps/member/forms.py @@ -1,7 +1,11 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later +import io + +from PIL import Image from django import forms +from django.conf import settings from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.models import User from django.forms import CheckboxSelectMultiple @@ -77,6 +81,38 @@ class ImageForm(forms.Form): width = forms.FloatField(widget=forms.HiddenInput()) height = forms.FloatField(widget=forms.HiddenInput()) + def clean(self): + """Load image and crop""" + cleaned_data = super().clean() + + # Image size is limited by Django DATA_UPLOAD_MAX_MEMORY_SIZE + image = cleaned_data.get('image') + if image: + # Let Pillow detect and load image + try: + im = Image.open(image) + except OSError: + # Rare case in which Django consider the upload file as an image + # but Pil is unable to load it + raise forms.ValidationError(_('This image cannot be loaded.')) + + # Crop image + x = cleaned_data.get('x', 0) + y = cleaned_data.get('y', 0) + w = cleaned_data.get('width', 200) + h = cleaned_data.get('height', 200) + im = im.crop((x, y, x + w, y + h)) + im = im.resize( + (settings.PIC_WIDTH, settings.PIC_RATIO * settings.PIC_WIDTH), + Image.ANTIALIAS, + ) + + # Save + image.file = io.BytesIO() + im.save(image.file, "PNG") + + return cleaned_data + class ClubForm(forms.ModelForm): def clean(self): diff --git a/apps/member/views.py b/apps/member/views.py index 4534c9e8..2a0394ff 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -1,10 +1,8 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later -import io from datetime import timedelta, date -from PIL import Image from django.conf import settings from django.contrib.auth import logout from django.contrib.auth.mixins import LoginRequiredMixin @@ -263,6 +261,7 @@ class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, Det return context def get_success_url(self): + """Redirect to profile page after upload""" return reverse_lazy('member:user_detail', kwargs={'pk': self.object.id}) def post(self, request, *args, **kwargs): @@ -271,26 +270,9 @@ class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, Det return self.form_valid(form) if form.is_valid() else self.form_invalid(form) def form_valid(self, form): + """Save image to note""" image_field = form.cleaned_data['image'] - x = form.cleaned_data['x'] - y = form.cleaned_data['y'] - w = form.cleaned_data['width'] - h = form.cleaned_data['height'] - # image crop and resize - image_file = io.BytesIO(image_field.read()) - # ext = image_field.name.split('.')[-1].lower() - # TODO: support GIF format - image = Image.open(image_file) - image = image.crop((x, y, x + w, y + h)) - image_clean = image.resize((settings.PIC_WIDTH, - settings.PIC_RATIO * settings.PIC_WIDTH), - Image.ANTIALIAS) - image_file = io.BytesIO() - image_clean.save(image_file, "PNG") - image_field.file = image_file - # renaming - filename = "{}_pic.png".format(self.object.note.pk) - image_field.name = filename + image_field.name = "{}_pic.png".format(self.object.note.pk) self.object.note.display_image = image_field self.object.note.save() return super().form_valid(form) From 15ed9d81d59704b1283454a5ea5fd2718e796f1d Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Sun, 6 Sep 2020 12:16:36 +0200 Subject: [PATCH 2/2] Check image size before sending it --- .../templates/member/picture_update.html | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/member/templates/member/picture_update.html b/apps/member/templates/member/picture_update.html index 7c9128ce..326e3651 100644 --- a/apps/member/templates/member/picture_update.html +++ b/apps/member/templates/member/picture_update.html @@ -55,12 +55,18 @@ SPDX-License-Identifier: GPL-3.0-or-later /* SCRIPT TO OPEN THE MODAL WITH THE PREVIEW */ $("#id_image").change(function (e) { if (this.files && this.files[0]) { - var reader = new FileReader(); - reader.onload = function (e) { - $("#modal-image").attr("src", e.target.result); - $("#modalCrop").modal("show"); + // Check the image size + if (this.files[0].size > 2*1024*1024) { + alert("Ce fichier est trop volumineux.") + } else { + // Read the selected image file + var reader = new FileReader(); + reader.onload = function (e) { + $("#modal-image").attr("src", e.target.result); + $("#modalCrop").modal("show"); + } + reader.readAsDataURL(this.files[0]); } - reader.readAsDataURL(this.files[0]); } }); @@ -104,4 +110,4 @@ SPDX-License-Identifier: GPL-3.0-or-later }); }); -{% endblock %} \ No newline at end of file +{% endblock %}