Django ImageField 文件保存时处理图像(剪切,再次上传,旋转等)

Posted Jason_WangYing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django ImageField 文件保存时处理图像(剪切,再次上传,旋转等)相关的知识,希望对你有一定的参考价值。

方法一:save时处理

使用Django的ImageField可以方便地生成图像字段,但DJANGO并没有提供相应的方法在图像文件上传时对图像进行处理,如图像裁剪,生成缩略图,在网上的找到的方法都是覆盖Model的save方法,如:

class Photo(models.Model):
      photo_file = models.ImageField(verbose_name=u'文件',upload_to='album/photos/%Y/%m/%d',max_length=255)
     descript = models.TextField(verbose_name=u"描述",blank=True)      
     def save(self, force_insert=False, force_update=False):
           filename = self.photo_file.path
           img = Image.open(filename)
           #进行图像处理
            ....
            ....
            super(Photo,self).save(force_insert, force_update) 

特殊说明:

  • self.photo_file.path这里返回的路径有问题,丢失了model里面的upload_to里面的路径,我找了半天也没找到原因,返回的绝对路径就是丢失了这部分
  • 我实际项目用的是相对路径,这里说明下,我刚开始是这样用的open('/media/album/photos/%Y/%m/%d'),结果发现怎么调用都是提示无此文档,我自己看了半天没找到问题,吃了个饭回来,看了眼发现是多了'/'在media前面,去掉即可
  • 我还发现了个问题,就是如果不调用save,图片是以数据流的形式存在内存中,没有写入文件,调用save后才写入文件
  • 我这里获取文件内容后还需要再次上传到微信的素材里面,获取素材id和url,再次存储到数据库里面,所以我是先save再save(update_fields='需要更新的字段'),如果再次save的话,默认是如果有数据更新所有数据,没有数据创建数据。

    def save(self, force_insert=False, force_update=False, using=None,update_fields=None):
        # 永久素材上传,获取media_id
        if self.images:
            temp = 'media/img/'+str(self.images)
            super().save()
            res = wechat_check.upload_add_media_of_image(temp)
            self.image_weixinid = res['media_id']
            self.url = res["url"]
            super().save(update_fields=['image_weixinid','url'])
        else:
#             没有图片,无需上传
            super().save()

上述方法可以在Model保存时获取ImageField的文件,采用PIL进行处理,但该方法有个缺点,就是当对descript字段进行修改更新时,save 方法被调用,将重新对图像进行处理,加重服务器负载.

方法二:封装类,给model.ImageField类再次封装,我们创建model时用新封装的类,在封装的类中,也是在save时修改数据


通过对Django代码的分析,采用定制ImageField的方法实现了同样功能并解决上述例子存在的缺陷:
继承ImageField生成新的图像字段类,用新的save方法覆盖ImageField的save方法,但ImageField对文件操作的方法大部分来自其父类FileField和ImageFieldFile:

#django/db/models/fields/files.py
class ImageField(FileField):
       attr_class = ImageFieldFile
       def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
           ....
           ....

  ImageField本身并不进行文件得理,要覆盖ImageField的save方法,需要先从ImageFieldFile下手,定义新的ImageFieldFile:

#生成用像类型,重载ImageField的SAVE事件,实现图像尺寸调整及自动生成缩略图

from django.db.models.fields.files import ImageFieldFile,FieldFile,FileField

class NewImageFieldFile(ImageFieldFile):
    def save(self, name, content, save=True):
        # Repopulate the image dimension cache.      
        temp_file = tempfile.TemporaryFile()
        f = StringIO.StringIO(content.read())
        image = Image.open(f)
        #取得指定的图像最大尺寸
        max_width,max_height = self.field.max_width,self.field.max_height
        #进行图像处理...
        ....
        image.save(temp_file,'JPEG')
        temp_file.seek(0)
        content2 = ContentFile(temp_file.read())
        content.close()
        temp_file.close()
        super(NewImageFieldFile, self).save(name, content2, save)

#生成新的ImageField
class NewImageField(models.ImageField):
    attr_class = NewImageFieldFile
    def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None,thumbnail_path=None,max_size=(600,600),**kwargs):
    self.width_field, self.height_field = width_field, height_field
    self.thumbnail_path = thumbnail_path
    self.max_width,self.max_height = max_size[0], max_size[1]
    models.ImageField.__init__(self, verbose_name, name, **kwargs)

修改class Photo,使用新的图像字段类:

class Photo(models.Model):
      photo_file = models.NewImageField(verbose_name=u'文件',upload_to='album/photos/%Y/%m/%d',max_length=255,max_size=(1024,800))
     descript = models.TextField(verbose_name=u"描述",blank=True)      

以上是关于Django ImageField 文件保存时处理图像(剪切,再次上传,旋转等)的主要内容,如果未能解决你的问题,请参考以下文章

Django ImageField 保存到位置

使用 Heroku 处理 Django 媒体文件

将通过 ajax 上传的文件保存到 Django 模型 ImageField

将解码的临时图像保存到 Django Imagefield

Django - 手动将图像保存到 ImageField 字段

Django ImageField 上传图片并保存到数据库