Skip to main content
 首页 » 编程设计

python之Django 在 stdimage 处理之前修改图像

2025年05月04日52arxive

我正在使用 django-stdimage 创建图像的变体。

class Photo(models.Model): 
    photo = StdImageField(upload_to='photos', verbose_name=_("photo"), 
                      variations={'large': (600, 600), 'thumbnail': (100, 100)} 

StdImageField 对图像进行自己的操作,子类化 ImageField 并具有 attr_class = StdImageFieldFile

StdImageFieldFile 执行实际的保存操作

class StdImageFieldFile(ImageFieldFile): 
    """ 
    Like ImageFieldFile but handles variations. 
    """ 
 
    def save(self, name, content, save=True): 
        super(StdImageFieldFile, self).save(name, content, save) 
        render_variations = self.field.render_variations 
        if callable(render_variations): 
            render_variations = render_variations( 
                file_name=self.name, 
                variations=self.field.variations, 
                storage=self.storage, 
            ) 
        if not isinstance(render_variations, bool): 
            msg = ( 
                '"render_variations" callable expects a boolean return value,' 
                ' but got %s' 
                ) % type(render_variations) 
            raise TypeError(msg) 
        if render_variations: 
            self.render_variations() 

但是,我想在 StdImageFieldFile 执行操作(旋转)之前对图像进行一些操作。

所以我创建了我的自定义字段,以便在将图像传递给 stdimage 之前捕获图像

class Rotate(ImageFieldFile): 
    def save(self, name, content, save=True): 
        save = False 
 
        return super(Rotate, self).save(name, content, save) 
 
class StdImageFieldFileRotateMixin(Rotate, StdImageFieldFile): 
    pass 
 
class StdImageFieldRotate(StdImageField): 
    attr_class = StdImageFieldFileRotateMixin 

我在 Rotate 类的 content 属性中有图像,我可以使用 PIL 操作图像,但完成后,我不知道如何将此图像分配回内容属性。它似乎是在较低级别生成的。是否有一种方法可以生成此 content 属性,然后 MRO 将处理其余部分(即将其传递给 StdImageFieldFile,它将完成其余部分)?

请您参考如下方法:

您可以将可调用对象传递给 StdImageField 类的参数 render_variations 并在那里预处理图像。

例如,假设您想要节省磁盘空间并将原始图像的大小调整为较小的版本,只将较小的图像与 django-stdimage 创建的变体一起使用。你可以这样写一个函数:

from io import BytesIO 
from PIL import Image 
 
from django.core.files.base import ContentFile 
 
from stdimage.utils import render_variations 
 
def preprocess(file_name, variations, storage): 
    with storage.open(file_name) as f: 
        with Image.open(f) as image: 
            file_format = image.format 
 
            # resize to a maximum of 1000x1000 keeping aspect ratio 
            image.thumbnail((1000, 1000), resample=Image.ANTIALIAS) 
 
            with BytesIO() as file_buffer: 
                image.save(file_buffer, file_format) 
                f = ContentFile(file_buffer.getvalue()) 
                # delete the original big image 
                storage.delete(file_name) 
                # save the resized version with the same filename and format 
                storage.save(file_name, f) 
 
    # render stdimage variations 
    render_variations(file_name, variations, replace=True, storage=storage) 
 
    return False  # prevent default rendering 

然后您可以将该函数传递给 StdImageField 类的 render_variations 参数,如下所示:

class Photo(models.Model): 
    photo = StdImageField(upload_to='photos', verbose_name=_("photo"), 
                  variations={'large': (600, 600), 'thumbnail': (100, 100)}, 
                  render_variations=preprocess) 

这项技术的灵感来自 Async image processing example在 Github 上的 django-stdimage 项目的自述文件中。

preprocess 函数中处理图像的方式也受到 stdimage.models.StdImageFieldFile.render_variation 的启发。方法并使用 Django 存储对象来处理图像文件的不同存储类型(想想 Amazon S3)。