2010-03-15 42 views
0

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

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

这里是代码(保存方法是对艺术家模型):

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) 

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

+0

假设'保存()'正在从Django模块调用时,它是很难在不调用代码来回答,或艺术家的定义.save,或ContentFile等 – msw 2010-03-15 02:25:03

+0

我把它的方式是,在shell: 艺术家= Artist.objects.get(ID = 1) artist.save() 那是怎样我一直在测试如果它有效,艺术家就会有一个形象。所以如果self.image是真的。 这是在Artist模型上定义的保存方法,与ContentFile一样:从django.core.files.base导入ContentFile – tsoporan 2010-03-15 02:45:14

回答

3

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

此模板过滤器应该在第一次加载时生成文件,然后在将来加载时加载文件。它很久以前从某个博客借来的,虽然我认为我添加了中心裁剪功能。最有可能的其他功能更多。

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

文件应用程序的名字/ 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) 
+1

+1显示我没有想到的事情。我倾向于在模型层做这种事情,但我现在认为你的模板方法可能更有意义(但如果你有一个相对高流量的网站,你会想要缓存,就像疯了一样,因为检查文件修改时间不是免费的)。 – 2010-03-15 09:18:25

+0

@dominic我同意缓存,尽管缓存修改日期应该很简单,并且每当用户进行更改时都会手动将其无效(为了您的目的,希望这不会很常见); – Jiaaro 2010-03-15 14:49:05

+0

好点。 getmtime子句可能可能被删除或更改。所有需要的是在修改图像字段时的清理例程。我认为在我的系统中,如果图像字段文件被更改,它将以新文件名(和miniature_filename)结尾。 – michael 2010-03-15 22:23:40