在Django中从同一图像生成两个缩略图

时间:2010-03-15 02:10:30

标签: python django python-imaging-library thumbnails

这似乎是一个非常容易的问题,但我无法弄清楚这里发生了什么。 基本上,我想要做的是在Django模型上从一个图像创建两个不同的缩略图。最终发生的事情是,它似乎是循环并重新创建相同的图像(每次向它附加一个下划线),直到它抛出一个文件名大的错误。所以,你最终得到的结果是:

OSError: [Errno 36] File name too long: 'someimg________________etc.jpg'

以下是代码(保存方法在Artist模型上):

def save(self, *args, **kwargs):

  if self.image:
    iname = os.path.split(self.image.name)[-1]
    fname, ext = os.path.splitext(iname)
    tlname, tsname = fname + '_thumb_l' + ext, fname + '_thumb_s' + ext
    self.thumb_large.save(tlname, make_thumb(self.image, size=(250,250)))
    self.thumb_small.save(tsname, make_thumb(self.image, size=(100,100)))
  super(Artist, self).save(*args, **kwargs)

 def make_thumb(infile, size=(100,100)):
   infile.seek(0)
   image = Image.open(infile)

   if image.mode not in ('L', 'RGB'):
     image.convert('RGB')

   image.thumbnail(size, Image.ANTIALIAS)

   temp = StringIO()
   image.save(temp, 'png')

   return ContentFile(temp.getvalue())

为了简洁起见,我没有展示进口产品。假设Artist模型上有两个ImageField:thumb_large和thumb_small。

我测试的方式是否有效,在shell中:

artist = Artist.objects.get(id=1)
artist.save() 
#error here after a little wait (until I assume it generates enough images that the OSError gets raised)

如果这不是正确的方法,我会很感激任何反馈。谢谢!

1 个答案:

答案 0 :(得分:3)

通常我喜欢尽可能为模板作者提供缩略图功能。这样他们就可以调整模板中的东西大小。而将其构建到业务逻辑层则更加固定。你可能有理由。

此模板过滤器应在首次加载时生成文件,然后在将来加载时加载文件。虽然我认为我添加了中心裁剪功能,但它很久以前就借鉴了一些博客。其他人很可能拥有更多功能。

{% load thumbnailer %}
...
<img src="{{someimage|thumbnail_crop:'200x200'}}" />

file appname / templatetags / thumbnailer.py

import os
import Image
from django.template import Library

register.filter(thumbnail)
from settings import MEDIA_ROOT, MEDIA_URL

def thumbnail_crop(file, size='104x104', noimage=''):
    # defining the size
    x, y = [int(x) for x in size.split('x')]
    # defining the filename and the miniature filename
    try:
        filehead, filetail = os.path.split(file.path)
    except:
        return '' # '/media/img/noimage.jpg'

    basename, format = os.path.splitext(filetail)
    #quick fix for format
    if format.lower() =='.gif':
        return (filehead + '/' + filetail).replace(MEDIA_ROOT, MEDIA_URL)

    miniature = basename + '_' + size + format
    filename = file.path
    miniature_filename = os.path.join(filehead, miniature)
    filehead, filetail = os.path.split(file.url)
    miniature_url = filehead + '/' + miniature
    if os.path.exists(miniature_filename) and os.path.getmtime(filename)>os.path.getmtime(miniature_filename):
        os.unlink(miniature_filename)
    # if the image wasn't already resized, resize it
    if not os.path.exists(miniature_filename):
        try:
            image = Image.open(filename)
        except:
            return noimage

        src_width, src_height = image.size
        src_ratio = float(src_width) / float(src_height)
        dst_width, dst_height = x, y
        dst_ratio = float(dst_width) / float(dst_height)

        if dst_ratio < src_ratio:
            crop_height = src_height
            crop_width = crop_height * dst_ratio
            x_offset = float(src_width - crop_width) / 2
            y_offset = 0
        else:
            crop_width = src_width
            crop_height = crop_width / dst_ratio
            x_offset = 0
            y_offset = float(src_height - crop_height) / 3
        image = image.crop((x_offset, y_offset, x_offset+int(crop_width), y_offset+int(crop_height)))
        image = image.resize((dst_width, dst_height), Image.ANTIALIAS)
        try:
            image.save(miniature_filename, image.format, quality=90, optimize=1)
        except:
            try:
                image.save(miniature_filename, image.format, quality=90)
            except:
                return '' #'/media/img/noimage.jpg'

    return miniature_url

register.filter(thumbnail_crop)