the5fire

关注Python、Django、Vim、Linux、Web开发、团队管理和互联网--Life is short, we need Python.


服务端图片处理神器Django-imagekit介绍

作者:the5fire | 标签:         | 发布:2014-09-10 7:13 a.m. | 阅读量: 12779, 12434

这两天做了一个定制django-imagekit生成图片名称的任务,本来想简单写写《如何定制Django-imagekit的ImageSpecField名称规则》,但是想到这个东西这么吊,还是多废话几句吧。

先说说场景,无论是做哪种业务,但凡是涉及到图片的,一定会有裁图的需求。但是裁图的需求又分好几种,主要的两种是:一种是被动裁图,可以裁出各种尺寸或限定的几种尺寸;另外一种是主动裁图,裁出固定的几种尺寸。

根据以往的项目实践中,我们往往是采用前一种做法,通过nignx的一个裁图模块image_filter,根据URL的参数来裁图,比方说,有原图:/images/the5fire.png, 通过请求 /images/the5fire.png/90x90 就可以得到一个90x90的方图,这是一例,被动裁图——接受到用户请求之后才处理图片。

另外一种是主动裁图,通过nginx的模块也能做,做法就是程序先自动去请求一遍。这种方式的另外一种做法就是自己在程序中裁图,对于读多写少的应用来说还挺合适。在Python程序中怎么处理裁图的需求呢?有个Pillow的库,也是很强大。如果在Django中处理,那就要考虑是不是有人已经在Pillow上做好了更符合Django流程的封装了,Django-imagekit就是这么个东西!

按照我们的需求,提供几种比例相同尺寸不同的图片,用Django-imagekit完全满足需求,当然最重要的是,很易用。

举个例子,定义个Model:

from django.db import models
from imagekit.models import ImageSpecField
from imagekit.processors import ResizeToFill

class Author(models.Model):
    avator = models.ImageField(
        upload_to="horizontal", blank=True,
        verbose_name="原图")
    avator_90x90 = ImageSpecField(
        source="avator", processors=[ResizeToFill(90, 90)],
        format='JPEG', options={'quality': 95})
    avator_30x30 = ImageSpecField(
        source="avator", processors=[ResizeToFill(30, 30)],
        format='JPEG', options={'quality': 95})

    # ImageSpecField不会生成数据库中的表

这样定义完之后,在xadmin后台我只需要上传一个原图,在访问的时候会自动生成90x90和30x30的图,访问的时候只需要author.avator_90x90.url即可。当然在模板上Django-imagekit也提供的一些tags,可以直接在模板中进行图片处理。

之前说的是主动裁图的方式,imagekit提供了几种生成图的策略:内容获取,路径访问,原图保存。主动裁图就是使用原图保存时进行生成的策略。另外值得一提的是,它还支持异步的方式来生成图片,需要用到Celery。

一开始有说到自定义生成图的路径的问题。默认情况下,imagekit使用 imagekit.cachefiles.namers.source_name_as_path 来生成图片的路径。大体逻辑是,根据你设置的参数:processors,format,options进行pickle,把数据写到一个内存文件中,然后根据文件内容做一个md5,作为生成这个尺寸图片的名称。这么说比较抽象,还是举个例子:原图:/image/the5fire.png 生成图 /CACHE/images/the5fire/123dadwdniijonsd.jpg,这不是实际的生成结果,只是示例,最后的 123xxxsd 可以当做那个md5的值,当然前面的 /CACHE/images/ 这样的路径也可以定制。

按照这样的逻辑,使用imagekit的话就不需要再定义一个数据库字段来存放,只需要根据原图路径加上这个规则,就能取到对应的图片。

如果所有项目都用imagekit还好,默认的生成规则就行,但是如果其他项目需要用到你这个数据库中的内容,单独提供接口,并且不是使用Django甚至也不是Python开发,那这种逻辑就很复杂了。这种根据参数进行pickle,然后计算md5值的逻辑在其他项目中根本无法使用,因为其他项目没有你的这个Model定义。

基于此情景,需要自定义路径,也就是imagekit中的 namer 。自定义的逻辑很简单,只需要在Django中设置IMAGEKIT_SPEC_CACHEFILE_NAMER使用你自定义的方法,自定义的逻辑可以是这样, 简单copy并修改默认的source_name_as_path的代码即可:

def spec_namer(generator):
    try:
        width = generator.processors[0].width
        height = generator.processors[0].height
        quality = generator.options.get('quality', 'default')
        filename = '%s_%s_s' % (width, height, quality)
    except (IndexError, AttributeError):
        return source_name_as_path(generator)

    source_filename = getattr(generator.source, 'name', None)

    if source_filename is None or os.path.isabs(source_filename):
        # Generally, we put the file right in the cache file directory.
        dir = settings.IMAGEKIT_CACHEFILE_DIR
    else:
        # For source files with relative names (like Django media files),
        # use the source's name to create the new filename.
        dir = os.path.join(settings.IMAGEKIT_CACHEFILE_DIR,
                        os.path.splitext(source_filename)[0])

    ext = suggest_extension(source_filename or '', generator.format)

    return os.path.normpath(os.path.join(dir, '%s%s' % (filename, ext)))

这样就可以了,生成规则就是: CACHE/images/ + 原图路径 + 宽_高_质量 + .jpg 。 注意,设置format为JPEG,后缀就是jpg。

imagekit就简单介绍到这里,功能还有很多,比如水印,去边框等等。如果有这类的使用场景,不妨试试:

http://django-imagekit.readthedocs.org/en/latest/index.html

- from the5fire.com
----EOF-----

微信公众号:Python程序员杂谈


其他分类: