在Django中从同一图像生成两个缩略图
这个问题看起来挺简单的,但我就是搞不明白到底发生了什么。基本上,我想在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。
我测试这个是否有效的方法是在命令行中:
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 个回答
3
一般来说,我喜欢让模板的作者尽可能地拥有缩略图的功能。这样他们就可以根据需要调整模板中内容的大小。而如果把这个功能放在业务逻辑层,就会比较固定,可能不太灵活。不过,你可能有自己的理由。
这个模板过滤器应该在第一次加载时生成文件,然后在以后的加载中直接使用这个文件。这个想法是从很久以前的一个博客借来的,虽然我觉得我加了一个居中裁剪的功能。可能还有其他人做得更好,功能更多。
{% load thumbnailer %}
...
<img src="{{someimage|thumbnail_crop:'200x200'}}" />
文件路径是 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)