Django保存图像时自动压缩的实现方案

一只小鹿
一只小鹿
发布时间:2026-10-13 | 5人浏览过
原创

本文详解如何在Django中利用Pillow库和pre_save信号,在保存ImageField时自动压缩图像。通过调整JPEG质量参数及结合尺寸调整,有效减小文件体积,提升网站加载速度并节省服务器存储空间。

Django图像自动压缩方案

假设我们有一个简单的Post模型,包含图像及其描述字段。


from django.db import models
 
class Post(models.Model):
 text = models.TextField()
 image = models.ImageField(upload_to='images/')

为了优化图像大小,我们需要对Post模型的image字段进行处理。这样做有充分的理由:它有助于加快网站或应用程序的加载速度,并减少服务器存储占用。在介绍Django的具体实现之前,我们先简要概述如何使用Pillow进行图像压缩。

Pillow基础压缩

使用Pillow压缩图像

Pillow是处理图像相关操作的优秀Python库。Image类提供了用于图像输入输出和操作的方法。Image.open可以从文件路径或文件对象读取图像。Image类的save方法允许指定quality参数,该参数用于以jpg格式保存图像,取值范围为1到95,默认值为75。如果设置的质量值大于95,会导致生成的图像尺寸反而比原图更大。


from PIL import Image
im = Image.open('/some/path/to/image')
im.save('/desired/path/new_image_name.jpg', quality=70)
im.close()

仅使用quality参数并非减小文件大小的唯一方法。例如,可以将其与调整图像尺寸相结合,从而获得更小的图像文件。

Django信号处理实现

利用Django信号

信号机制允许某些发送者通知一组接收者已经发生了特定事件。

Django内置了许多信号,目前我们关注的是django.db.models.signals.pre_save信号,该信号在调用模型的save()方法之前发送。要将处理程序连接到信号,可以使用Signal.connect方法。若要将信号附加到特定的sender(在本例中为模型),必须向Signal.connect方法提供sender参数。例如,将pre_save信号附加到上述定义的Post模型,代码如下:

pre_save.connect(our_handler, sender=Post)

Django还提供了用于连接信号的接收器装饰器,这使得代码更加符合Python惯用法。因此,除了定义our_handler并进行连接外,我们还可以使用装饰器来定义our_handler,如下所示:


from django.dispatch import receiver
...
 
@receiver(pre_save, sender=Post)
def my_handler(sender, **kwargs):
 ...

现在,让我们完善处理程序以压缩图像。pre_save信号还会向处理函数传递instance参数,该参数对应于实际要保存的实例。这在检查字段是否已更新时特别有用,因为我们不希望重复压缩图像。因此,处理程序功能可以设置为:


from django.db.models.signals import pre_save
from django.dispatch import receiver
 
@receiver(pre_save, sender=Post)
def handle_image_compression(sender, instance, **kwargs):
 try:
  post_obj = Post.objects.get(pk=instance.pk)
 except Post.DoesNotExist:
  # the object does not exists, so compress the image
  instance.image = compress_image(instance.image)
 else:
  # the object exists, so check if the image field is updated
  if post_obj.image != instance.image:
   instance.image = compress_image(instance.image)

最后一步是编写compress_image函数,该函数接收一个ImageField并返回一个新的ImageField。PIL的Image.open()方法仅适用于文件路径或文件对象。这里有一个有趣的事实:ImageField的超类镜像了Python的File API,因此我们可以像使用实际文件一样使用它。解决了Image.open的问题后,接下来是Image.save。事实证明,Image.save可以将图像写入BytesIO对象。因此,我们的图像压缩功能变为:


from PIL import Image
from io import BytesIO
from django.core.files import File
def compress_image(image):
 im = Image.open(image)
 out = BytesIO()
 im.save(out, 'JPEG', quality=70)
 compressed = File(out, name=image.name)
 im.close()
 return compressed

总结

以上介绍了在Django保存图像时同时压缩图像的示例代码详解,希望对大家有所帮助!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表