Resize and convert Django images before uploading

I searched a lot for this topic, but could not find what I needed. I will explain my problem:

On my website a user can upload an image. I need to resize an image and convert it to jpeg up by downloading it.

I saw some solutions using the method in Views.py, but I would like to do this by overriding the save () method in my Models.py. The problem is that all the solutions that I found for resizing the image in the save method do this after saving it for the first time (calling super-phoning), which means that it will use the bandwidth (if I use CDN) neither what (am I right on that point?).

thanks for the help

+8
source share
7 answers

First, it’s best to set the correct language. Django and Python exist only on the server side. Therefore, everything that they manipulate, save or otherwise use must first be sent to the server. If Django or Python must manage the photo, the user MUST first upload the photo to the server. After uploading a photo, Django may make changes before saving the file.

If your problem is with download bandwidth and you do not want large files to load, you will have to resize and reformat the photo on the client side. If this is a web application, this can be done using Javascript, but it cannot be done using Python, because Python does not work on the client side for an application like yours.

If your concern is not bandwidth, you can ask the user to β€œdownload” the file, but then Django resizes it and formats it before saving.

You are correct that you want to override the save function for the photo object. I would recommend using a library to handle resizing and reformatting, such as sorl .

from sorl.thumbnail import ImageField, get_thumbnail class MyPhoto(models.Model): image = ImageField() def save(self, *args, **kwargs): if self.image: self.image = get_thumbnail(self.image, '500x600', quality=99, format='JPEG') super(MyPhoto, self).save(*args, **kwargs) 

Sorl is just a library that I'm sure and familiar with, but it does require some tweaking and tweaking. You can check Pillow or something instead, and just replace the line that overlaps self.image .

I also found a similar question here .

Edit: saw the update of your comment answer above. Also note that if your web server processes Django and your files are stored in the database on some CDN, this method will work. The image will be resized on the web server before uploading it to the CDN (assuming that your configuration meets my expectations).

Hope this helps!

+22
source

Here is an application that can take care of this: django-smartfields . It also deletes the old image whenever a new one is loaded.

 from django.db import models from smartfields import fields from smartfields.dependencies import FileDependency from smartfields.processors import ImageProcessor class ImageModel(models.Model): image = fields.ImageField(dependencies=[ FileDependency(processor=ImageProcessor( format='JPEG', scale={'max_width': 300, 'max_height': 300})) ]) 
+7
source

EDIT4: full working code can be found here (the code below still has some errors)

Still struggling, but there is progress ^^: I'm trying to make resizing and converting in memory using PIL (and, seeing the number of questions on this issue, it seems that I'm not the only one). My code so far (in Models.py):

 from PIL import Image as Img import StringIO from django.core.files import File class Mymodel(models.Model): #blablabla photo = models.ImageField(uppload_to="...", blank=True) def save(self, *args, **kwargs): if self.photo: image = Img.open(StringIO.StringIO(self.photo.read())) image.thumbnail((100,100), Img.ANTIALIAS) output = StringIO.StringIO() image.save(output, format='JPEG', quality=75) output.seek(0) self.photo = File(output, self.photo.name()) super(Mymodel, self).save(*args, **kwargs) 

I have an error on "photo = File (output, photo.name ())"

thanks

EDIT: Sorry, it was a simple mistake (forgot about yourself), fixed in the code. Now I have the error "TypeError, the unicode object cannot be called" on the same line.

EDIT2: Saw something about "output.getvalue ()" here , but really don't know if there could be any help.

EDIT3: Solved! The code is below.

 from PIL import Image as Img import StringIO from django.core.files.uploadedfile import InMemoryUploadedFile class Mymodel(models.Model): photo = models.ImageField(upload_to="...", blank=True) def save(self, *args, **kwargs): if self.photo: image = Img.open(StringIO.StringIO(self.photo.read())) image.thumbnail((200,200), Img.ANTIALIAS) output = StringIO.StringIO() image.save(output, format='JPEG', quality=75) output.seek(0) self.photo= InMemoryUploadedFile(output,'ImageField', "%s.jpg" %self.photo.name, 'image/jpeg', output.len, None) super(Mymodel, self).save(*args, **kwargs) 

For PNG looking weird, see the second post in this thread

+5
source

Just putting this together for my own project took me a while before I realized that bytes and image are separate attributes on Django ImageField, this solution worked for me with Python 3.6.

  def clean_image(self): image_field = self.cleaned_data.get('image') if image_field: try: image_file = BytesIO(image_field.file.read()) image = Image.open(image_file) image.thumbnail((300, 300), Image.ANTIALIAS) image_file = BytesIO() image.save(image_file, 'PNG') image_field.file = image_file image_field.image = image return image_field except IOError: logger.exception("Error during resize image") 
+3
source
 from django.db import models from django.contrib.auth.models import User from PIL import Image class profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) bio = models.CharField(max_length=300) location = models.CharField(max_length=99) image = models.ImageField(default='default.jpg', upload_to='profile_pics') def save(self): super().save() # saving image first img = Image.open(self.image.path) # Open image using self if img.height > 300 or img.width > 300: new_img = (300, 300) img.thumbnail(new_img) img.save(self.image.path) # saving image at the same path 

This example shows how to load an image after changing its size. Change the new_img pixel as you wish.

+2
source

It worked for me, try it. I am using Django 2.0.6 and Python 3.6.4

 from django.db import models from PIL import Image from io import BytesIO from django.core.files.uploadedfile import InMemoryUploadedFile class ImageUpload(models.Model): name = models.CharField(max_length=100) uploadedImage = models.ImageField(upload_to='Uploads/%Y/%m/', db_column="Upload an Image") def save(self, *args, **kwargs): imageTemproary = Image.open(self.uploadedImage) outputIoStream = BytesIO() imageTemproaryResized = imageTemproary.resize( (1020,573) ) imageTemproaryResized.save(outputIoStream , format='JPEG', quality=85) outputIoStream.seek(0) self.uploadedImage = InMemoryUploadedFile(outputIoStream,'ImageField', "%s.jpg" %self.uploadedImage.name.split('.')[0], 'image/jpeg', sys.getsizeof(outputIoStream), None) super(ImageUpload, self).save(*args, **kwargs) 
+1
source

Here is another package that works for me with minimal code modification - django-resized .

models.py

 from django_resized import ResizedImageField class Post(models.Model): image = ResizedImageField(upload_to='uploads/%Y/%m/%d') 

settings.py

 DJANGORESIZED_DEFAULT_SIZE = [1024, 768] DJANGORESIZED_DEFAULT_QUALITY = 75 DJANGORESIZED_DEFAULT_KEEP_META = True DJANGORESIZED_DEFAULT_FORCE_FORMAT = 'JPEG' DJANGORESIZED_DEFAULT_FORMAT_EXTENSIONS = {'JPEG': ".jpg"} DJANGORESIZED_DEFAULT_NORMALIZE_ROTATION = True 

It!

0
source

Source: https://habr.com/ru/post/971254/


All Articles