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

29 投票
10 回答
58292 浏览
提问于 2025-04-18 10:48

我在这个话题上搜索了很多,但还是找不到我需要的东西。让我来解释一下我的问题:

在我的网站上,用户可以上传一张图片。我需要在上传之前,把这张图片调整大小并转换成JPEG格式上传之前。

我看到一些解决方案是使用Views.py中的方法,但我想通过重写Models.py中的save()方法来实现。问题是,我找到的所有在save方法中调整图片大小的解决方案都是在第一次保存之后进行的(调用super函数),这意味着如果我使用CDN的话,会浪费带宽(我这样理解对吗?)。

谢谢你的帮助。

10 个回答

7

编辑4:完整的工作代码可以在这里找到(下面的代码还有一些错误)

我还在努力,但有一些进展^^:我正在尝试用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)

我在“photo = File(output, photo.name())”这一行遇到了错误

谢谢

编辑:抱歉,这是一个简单的错误(忘记加self.),已经在代码中修正了。现在我在同一行遇到了“TypeError,'unicode'对象不可调用”的错误。

编辑2:看到关于“output.getvalue()”的内容在这里,但我不太确定这是否有帮助。

编辑3:解决了!!下面是代码。

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)

关于PNG看起来奇怪的问题,请查看这个帖子的第二个回复

9

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

这里还有一个对我来说很好用的包,几乎不需要修改代码 - 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

就这样!

19
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

这个例子展示了如何在调整图片大小后上传图片。你可以根据自己的需要,随意修改新图片的像素大小。

48

首先,咱们得搞清楚用什么语言。Django和Python只在服务器上工作。所以,它们处理、保存或者使用的任何东西,都必须先发送到服务器。如果Django或Python要管理照片,用户必须先把照片上传到服务器。一旦照片上传完毕,Django就可以对它进行修改,然后再保存这个文件。

如果你担心上传的带宽,不想让大文件被上传,那你就得在客户端先调整照片的大小和格式。如果这是一个网页应用,可以用JavaScript来完成这个操作,但Python不能在客户端执行,因为对于你这样的应用,Python只在服务器上工作。

如果你不担心带宽问题,那用户可以直接“上传”文件,之后让Django在保存之前调整大小和格式。

你说得对,你需要重写照片对象的保存功能。我建议使用一个库来处理调整大小和格式的问题,比如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是我比较熟悉的一个库,但它需要一些调试和配置。你也可以看看Pillow之类的库,只需替换掉重写的self.image那一行就行。

我还发现了一个类似的问题在这里

补充:我看到你上面评论的更新了。如果你的网页服务器在处理Django,并且你的文件是保存到某个CDN上,这种方法是可行的。图片会在上传到CDN之前在网页服务器上被调整大小(假设你的配置和我想的差不多)。

希望这对你有帮助!

撰写回答