Django保存图像时自动压缩的实现方案
本文详解如何在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保存图像时同时压缩图像的示例代码详解,希望对大家有所帮助!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









