2010-03-15 43 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