使用Django进行图片缩放?

14 投票
12 回答
38951 浏览
提问于 2025-04-15 13:04

我刚开始接触Django(还有Python),在尝试自己搞明白一些事情之前,我不想直接使用别人的应用。我现在有点困惑,不太明白在Django(或者Python)中,事情是怎么安排的。我想弄清楚的是,如何在上传图片后调整它的大小。我已经把我的模型设置得很好,并且在后台管理中也能正常使用,图片也能顺利上传到指定的文件夹:

from django.db import models

# This is to list all the countries
# For starters though, this will be just United Kingdom (GB)
class Country(models.Model):
    name = models.CharField(max_length=120, help_text="Full name of country")
    code = models.CharField(max_length=2, help_text="This is the ISO 3166 2-letter country code (see: http://www.theodora.com/country_digraphs.html)")
    flag = models.ImageField(upload_to="images/uploaded/country/", max_length=150, help_text="The flag image of the country.", blank=True)

    class Meta:
        verbose_name_plural = "Countries"

    def __unicode__(self):
        return self.name

现在我遇到的问题是,如何把这个文件做成一个缩略图。正如我所说的,我希望在不使用其他人应用的情况下(暂时),自己实现这个功能。我从DjangoSnippets上找到了这段代码:

from PIL import Image
import os.path
import StringIO

def thumbnail(filename, size=(50, 50), output_filename=None):
    image = Image.open(filename)
    if image.mode not in ('L', 'RGB'):
        image = image.convert('RGB')
    image = image.resize(size, Image.ANTIALIAS)

    # get the thumbnail data in memory.
    if not output_filename:
        output_filename = get_default_thumbnail_filename(filename)
    image.save(output_filename, image.format) 
    return output_filename

def thumbnail_string(buf, size=(50, 50)):
    f = StringIO.StringIO(buf)
    image = Image.open(f)
    if image.mode not in ('L', 'RGB'):
        image = image.convert('RGB')
    image = image.resize(size, Image.ANTIALIAS)
    o = StringIO.StringIO()
    image.save(o, "JPEG")
    return o.getvalue()

def get_default_thumbnail_filename(filename):
    path, ext = os.path.splitext(filename)
    return path + '.thumb.jpg'

...但这让我更加困惑了...因为我不知道这段代码应该如何融入到我的Django应用中?而且,这真的是制作成功上传的图片缩略图的最佳解决方案吗?有没有人能给我展示一个简单、可靠的方法,让像我这样的初学者能够正确学习这个?比如,应该把这段代码放在哪里(models.py?forms.py?...)以及它在实际应用中是如何工作的?...我只是需要一点帮助,来理解和解决这个问题。

谢谢!

12 个回答

3

我对你发的代码不太确定,因为我从来不使用那种模型,不过还有另一种方法。

你可以自己实现一个 FileUploadHandler 来处理图片文件的上传。示例可以在这里找到。就在第37行(dest.close())之后,使用 thumbnail(upload_dir + upload.name) 这个函数(就是你发的那个)。

希望这对你有帮助。

4

这是我在我的模型中用来保存新缩略图的代码,前提是上传的图片发生了变化。这段代码是基于另一个Django的代码片段写的,但我记不清是谁写的。如果你知道的话,请留言告诉我,这样我可以给他们一些认可。

from PIL import Image
from django.db import models
from django.contrib.auth.models import User

import os
import settings

class Photo_Ex(models.Model):
    user = models.ForeignKey(User, blank=True, null=True)    
    photo = models.ImageField(upload_to='photos')
    thumbnail = models.ImageField(upload_to='profile_thumb', blank=True,
                              null=True, editable=False)

    def save(self, *args, **kwargs):
        size = (256,256)
        if not self.id and not self.photo:
            return

        try:
            old_obj = Photo_Ex.objects.get(pk=self.pk)
            old_path = old_obj.photo.path
        except:
            pass

        thumb_update = False
        if self.thumbnail:
            try:
                statinfo1 = os.stat(self.photo.path)
                statinfo2 = os.stat(self.thumbnail.path)
                if statinfo1 > statinfo2:
                    thumb_update = True
            except:
                thumb_update = True

        pw = self.photo.width
        ph = self.photo.height
        nw = size[0]
        nh = size[1]

        if self.photo and not self.thumbnail or thumb_update:
            # only do this if the image needs resizing
            if (pw, ph) != (nw, nh):
                filename = str(self.photo.path)
                image = Image.open(filename)
                pr = float(pw) / float(ph)
                nr = float(nw) / float(nh)

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

                if pr > nr:
                    # photo aspect is wider than destination ratio
                    tw = int(round(nh * pr))
                    image = image.resize((tw, nh), Image.ANTIALIAS)
                    l = int(round(( tw - nw ) / 2.0))
                    image = image.crop((l, 0, l + nw, nh))
                elif pr < nr:
                    # photo aspect is taller than destination ratio
                    th = int(round(nw / pr))
                    image = image.resize((nw, th), Image.ANTIALIAS)
                    t = int(round(( th - nh ) / 2.0))
                    image = image.crop((0, t, nw, t + nh))
                else:
                    # photo aspect matches the destination ratio
                    image = image.resize(size, Image.ANTIALIAS)

            image.save(self.get_thumbnail_path())
            (a, b) = os.path.split(self.photo.name)
            self.thumbnail = a + '/thumbs/' + b
            super(Photo_Ex, self).save()
            try:
                os.remove(old_path)
                os.remove(self.get_old_thumbnail_path(old_path))
            except:
                pass

    def get_thumbnail_path(self):
        (head, tail) = os.path.split(self.photo.path)
        if not os.path.isdir(head + '/thumbs'):
            os.mkdir(head + '/thumbs')
        return head + '/thumbs/' + tail

    def get_old_thumbnail_path(self, old_photo_path):
        (head, tail) = os.path.split(old_photo_path)
        return head + '/thumbs/' + tail   
9

如果你觉得可以的话,这里有一个现成的Django应用,它正好可以满足你的需求:https://github.com/sorl/sorl-thumbnail

撰写回答