Django Image upload and resize
Asked Answered
S

6

4

I have a standard Django form with an image field. When the image is uploaded, I would like to make sure that the image is no larger than 300px by 300px. Here is my code:

def post(request):
    if request.method == 'POST':
        instance = Product(posted_by=request.user)
        form = ProductModelForm(request.POST or None, request.FILES or None)
        if form.is_valid():
           new_product = form.save(commit=False)
           if 'image' in request.FILES:
              img = Image.open(form.cleaned_data['image'])
              img.thumbnail((300, 300), Image.ANTIALIAS)

              # this doesnt save the contents here...
              img.save(new_product.image)

              # ..because this prints the original width (2830px in my case)
              print new_product.image.width

The problem I am facing is, it is not clear to me how I get the Image type converted to the type that ImageField type.

Store answered 11/8, 2011 at 4:6 Comment(1)
Forgive me if i have this wrong i don't use Pil much/in a while. After you do a form.save() it puts it in your media folder somewhere depending on your settings. Why can't you then go an alter it there and resave it on top? You seem to be taking a copy of it from the response.Bucovina
C
3

From the documentation on ImageField's save method:

Note that the content argument should be an instance of django.core.files.File, not Python's built-in file object.

This means you would need to convert the PIL.Image (img) to a Python file object, and then convert the Python object to a django.core.files.File object. Something like this (I have not tested this code) might work:

img.thumbnail((300, 300), Image.ANTIALIAS)

# Convert PIL.Image to a string, and then to a Django file
# object. We use ContentFile instead of File because the
# former can operate on strings.
from django.core.files.base import ContentFile
djangofile = ContentFile(img.tostring())
new_product.image.save(filename, djangofile)
Counterpoise answered 11/8, 2011 at 6:59 Comment(0)
W
1

How about using standard image field https://github.com/humanfromearth/django-stdimage

Warhol answered 11/8, 2011 at 6:55 Comment(1)
The link above is broken.Berry
N
1

There you go, just change a little bit to suit your need:

class PhotoField(forms.FileField, object):

    def __init__(self, *args, **kwargs):
        super(PhotoField, self).__init__(*args, **kwargs)
        self.help_text = "Images over 500kb will be resized to keep under 500kb limit, which may result in some loss of quality"

    def validate(self,image):
        if not str(image).split('.')[-1].lower() in ["jpg","jpeg","png","gif"]:
            raise ValidationError("File format not supported, please try again and upload a JPG/PNG/GIF file")

    def to_python(self, image):
        try:
            limit = 500000
            num_of_tries = 10
            img = Image.open(image.file)
            width, height = img.size
            ratio = float(width) / float(height)

            upload_dir = settings.FILE_UPLOAD_TEMP_DIR if settings.FILE_UPLOAD_TEMP_DIR else '/tmp'
            tmp_file = open(os.path.join(upload_dir, str(uuid.uuid1())), "w")
            tmp_file.write(image.file.read())
            tmp_file.close()

            while os.path.getsize(tmp_file.name) > limit:
                num_of_tries -= 1
                width = 900 if num_of_tries == 0 else width - 100
                height = int(width / ratio)
                img.thumbnail((width, height), Image.ANTIALIAS)
                img.save(tmp_file.name, img.format)
                image.file = open(tmp_file.name)
                if num_of_tries == 0:
                    break                    
        except:
            pass
        return image

Source: http://james.lin.net.nz/2012/11/19/django-snippet-reduce-image-size-during-upload/

Nudicaul answered 22/11, 2012 at 18:4 Comment(0)
C
1

Here is an app that can take care of that: django-smartfields

from django.db import models

from smartfields import fields
from smartfields.dependencies import FileDependency
from smartfields.processors import ImageProcessor

class Product(models.Model):
    image = fields.ImageField(dependencies=[
        FileDependency(processor=ImageProcessor(
            scale={'max_width': 300, 'max_height': 300}))
    ])
Convalesce answered 24/12, 2014 at 4:35 Comment(0)
S
0

Try my solution here: https://mcmap.net/q/618823/-how-to-resize-the-new-uploaded-images-using-pil-before-saving

Highlight

  • Using Pillow for image processing (two packages required: libjpeg-dev, zlib1g-dev)
  • Using Model and ImageField as storage
  • Using HTTP POST or PUT with multipart/form
  • No need to save the file to disk manually.
  • Create multiple resolutions and stores their dimensions.
Soria answered 9/8, 2014 at 19:29 Comment(0)
S
0

You can use my library django-sizedimagefield for this, it has no extra dependency and is very simple to use.

Site answered 27/6, 2017 at 13:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.