Django ImageField 使用及其性能问题

2020-03-17

Djnago 自带的 ImageField 对处理图片非常实用。

  • ImageField 可以接受两个参数 width_fieldheight_field
  • 每当 ImageField 更新,会自动更新 heightwidth 信息
from django.db import models


class Photo(models.Model):
    image = models.ImageField(
        blank=True,
        null=True,
        height_field="height",
        width_field="width",
    )
    height = models.PositiveIntegerField(default=0)
    width = models.PositiveIntegerField(default=0)
    

有了这些信息,就可以对图片大小排序,过滤操作。

from django.db.models import Q

big_dim = 1024;
big_images = Photo.objects.filter(
    Q(height=big_dim) | Q(width=big_dim)
    )

缺点:

  • 增加图片 widthheight 信息每次查询都会把图片数据读到内存。
  • 如果是用的是 s3 类似的存储方式会对性能造成巨大伤害

解决方法:

  • 删除原本 Photo 字段中 width_fieldheight_field
  • 利用 Django Singals 的 pre_save 在存储图片前设置图片大小
from django.db import models
from django.db.models.signals import pre_save
from django.dispatch import receiver

class Photo(models.Model):
    image = models.ImageField(
        blank=True,
        null=True,
    )
    height = models.PositiveIntegerField(blank=True, null=True)
    width = models.PositiveIntegerField(blank=True, null=True)

@receiver(pre_save, sender=Photo)
def set_photo_dim(sender, instance: Photo, raw, **kwargs):
    if isinstance(instance, sender):
        setattr(instance, "width", instance.file.width)
        setattr(instance, "height", instance.file.height)