Django 图片上传和调整大小

4

我有一个标准的Django表单,其中包含一个图片字段。当上传该图片时,我希望确保图片大小不超过300px x 300px。以下是我的代码:

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

我面临的问题是,我不清楚如何将Image类型转换为ImageField类型。

如果我理解有误,请原谅,因为我已经很久没有使用Pil了。在执行form.save()之后,它会根据您的设置将其放在媒体文件夹中的某个位置。那么,为什么您不能在那里更改它并重新保存它呢?您似乎是从响应中复制了一份。 - James Khoury
6个回答

3

从ImageField的save方法文档中可以看到:

请注意,content参数应为django.core.files.File的实例,而不是Python内置的文件对象。

这意味着您需要将PIL.Image (img)转换为Python文件对象,然后将Python对象转换为django.core.files.File对象。类似这样的代码(我没有测试过)可能会起作用:

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)

1

这里有一个应用程序可以解决这个问题: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}))
    ])

1

好的,只需要稍微修改一下就可以满足您的需求:

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

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


1

上面的链接已经失效。 - rvnovaes

0

在这里尝试我的解决方案:https://dev59.com/l2sz5IYBdhLWcg3wVGPJ#25222000

亮点

  • 使用Pillow进行图像处理(需要两个软件包:libjpeg-dev,zlib1g-dev)
  • 使用Model和ImageField作为存储
  • 使用HTTP POST或PUT与multipart/form一起使用
  • 无需手动将文件保存到磁盘。
  • 创建多个分辨率并存储其尺寸。

0

你可以使用我的库django-sizedimagefield来实现这个功能,它没有额外的依赖项,并且非常容易使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接