Python Django 图像在上传前调整大小和转换

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24373341/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 04:30:11  来源:igfitidea点击:

Django image resizing and convert before upload

pythondjangodjango-models

提问by Raphael Laurent

I searched a lot on this subject but couldn't really find what I need. I'll explain my problem :

我在这个主题上搜索了很多,但找不到我需要的东西。我会解释我的问题:

On my website, the user can upload an image. I need to resize this image and convert it to JPEG beforeuploading it.

在我的网站上,用户可以上传图片。上传之前,我需要调整此图像的大小并将其转换为 JPEG 。

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

我在 Views.py 中看到了一些使用方法的解决方案,但我想通过覆盖 Models.py 中的 save() 方法来实现。问题是,我在 save 方法中找到的用于调整图像大小的所有解决方案都是在第一次保存(调用超级函数)后执行的,这意味着它会无用地使用带宽(如果我使用 CDN)(我对此是否正确)观点?)。

Thank you for your help

感谢您的帮助

采纳答案by Jamie Counsell

First, it's best to establish the correct language. Django and Python exist only on the server side. Therefore, anything they manipulate, save, or otherwise use, has to be first sent to the server. If Django or Python is to manage the photo, the user MUST upload this photo to the server first. Once the photo is uploaded, Django is free to make changes before storing the file.

首先,最好建立正确的语言。Django 和 Python 只存在于服务器端。因此,他们操作、保存或以其他方式使用的任何内容都必须首先发送到服务器。如果要使用 Django 或 Python 来管理照片,则用户必须先将此照片上传到服务器。上传照片后,Django 可以在存储文件之前自由进行更改。

If your concern is with upload bandwidth, and you don't want large files being uploaded, 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 can not be done with Python, since Python does not operate on the client side for an application like yours.

如果您担心上传带宽,并且不希望上传大文件,则必须在客户端调整照片大小并重新格式化。如果这是一个 Web 应用程序,这可以使用 Javascript 完成,但不能使用 Python 完成,因为 Python 不会在客户端运行像您这样的应用程序。

If your concern is not with bandwidth, then you're free to have the user "upload" the file, but then have Django resize and reformat it before saving.

如果您不关心带宽,那么您可以自由地让用户“上传”文件,但在保存之前让 Django 调整大小并重新格式化。

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

您想覆盖照片对象的保存功能是正确的。我建议使用库来处理调整大小和重新格式化,例如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 I am confident and familiar with, but it takes some tuning and configuration. You can check out Pillow or something instead, and just replace the line overriding self.image.

Sorl 只是我自信和熟悉的一个库,但它需要一些调整和配置。您可以查看 Pillow 或其他东西,只需替换覆盖self.image.

I also found a similar question here.

我也在这里找到了一个类似的问题。

Edit:saw the update to your comment response above. Also note that if your webserver is handling Django, and your files are being saved to a database on some CDN, this method will work. The image will be resized on the webserver beforebeing uploaded to your CDN (assuming your configuration is as I'm assuming).

编辑:在上面看到了对您的评论回复的更新。另请注意,如果您的网络服务器正在处理 Django,并且您的文件被保存到某些 CDN 上的数据库中,则此方法将起作用。图像将上传到 CDN之前在网络服务器上调整大小(假设您的配置与我假设的一样)。

Hope this helps!

希望这可以帮助!

回答by Raphael Laurent

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

EDIT4:可以在这里找到完整的工作代码(下面的代码仍然有一些错误)

Still fighting, but there is progress ^^ : I am trying to do the resizing and convertion in memory with PIL (and seeing the amount of questions on the subject, it seems that I am not the only one ^^). My code so far (in Models.py):

仍在战斗,但有进步^^:我正在尝试使用 PIL 进行内存中的调整大小和转换(并且看到有关该主题的问题数量,似乎我不是唯一一个 ^^)。到目前为止我的代码(在 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 at "photo = File(output, photo.name())"

我在“photo = File(output, photo.name())”有错误

Thanks

谢谢

EDIT : Sorry, it was a simple mistake (forgot the self.) corrected in code. Now I have the error "TypeError, 'unicode' object is not callable", at the same line.

编辑:对不起,这是一个简单的错误(忘记了自我。)在代码中更正。现在我在同一行出现错误“TypeError,'unicode' 对象不可调用”。

EDIT2: Saw something about "output.getvalue()" herebut don't really know if could be of any help.

EDIT2:在这里看到了一些关于“output.getvalue()”的信息,但不知道是否有任何帮助。

EDIT3 : Solved!! Code below.

EDIT3:解决了!!代码如下。

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 second post on this thread

对于 PNG 看起来很奇怪,请参阅此线程上的第二篇文章

回答by lehins

Here is an app that can take care of that: django-smartfields. It will also remove an old image whenever a new one is uploaded.

这是一个可以解决这个问题的应用程序:django-smartfields。每当上传新图像时,它也会删除旧图像。

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}))
    ])

回答by LanfeaR

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

只是将它放在我自己的项目中,花了我一段时间才意识到字节和图像是 Django ImageField 上的单独属性,这个解决方案适用于 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")

回答by Ken Mbogo

This Worked for me try it. I am using Django 2.0.6 and Python 3.6.4

这对我有用,试试吧。我正在使用 Django 2.0.6 和 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)

回答by Manish

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 upload image after image re-sizing. Change the pixel of new_img, whatever you want.

此示例显示如何在图像调整大小后上传图像。随意更改 new_img 的像素。

回答by Ramon Dekkers

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

这是另一个对我有用的软件包,只需最少的代码修改 - django-resized

models.py

模型.py

from django_resized import ResizedImageField

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

settings.py

设置.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

That's it!

就是这样!

回答by Jota

The easiest solution:

最简单的解决方案:

def compress(image):
    im = Image.open(image)
    # create a BytesIO object
    im_io = BytesIO() 
    # save image to BytesIO object
    im.save(im_io, 'JPEG', quality=70) 
    # create a django-friendly Files object
    new_image = File(im_io, name=image.name)
    return new_image