You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django更新记录时用PIL缩放图片遇ValueError: seek of closed file

Django更新Player模型时触发"seek of closed file"错误解决方法

问题背景

定义的Players模型包含图片字段,save方法会调用图片缩放函数,但更新操作时无论是否上传新图片,都会触发以下错误:

ValueError: seek of closed file

相关代码如下:

Players模型

class Players(models.Model):
    team = models.ForeignKey(Teams, verbose_name=_('Team'), on_delete=models.CASCADE)
    player_name = models.CharField(_('Player Name'),max_length=200)
    player_description = models.TextField(_('Player Description'))
    player_image = models.ImageField(_('Profile Pic'),upload_to='upload/player_image', null=True, blank=True)
    player_social = models.CharField(_('Social Media Tag'),max_length=200)

    class Meta:
        verbose_name = _("Players")
        verbose_name_plural = _("Players")

    def __str__(self):
        return self.team.team_name + ' - ' + self.player_name
    
    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)
        if self.player_image:
            image_resize(self.player_image, 250)

图片缩放函数

# Use PIL to resize images; set target width on each
def image_resize(image, tgt_width):
        img = PIL.Image.open(image)
        img.load()
        width, height = img.size
        target_width = tgt_width
        h_coefficient = width/tgt_width
        target_height = height/h_coefficient
        img = img.resize((int(target_width), int(target_height)), PIL.Image.ANTIALIAS)
        img.save(image.path, quality=100)
        img.close()
        image.close()

更新视图

@method_decorator(superuser_required, name='dispatch')
class PlayerUpdateView(UpdateView):
    model = Players
    template_name = 'scoreboard/players/player_update.html'
    form_class = PlayersForm
    context_object_name: str = 'player'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        team_id = Players.objects.get(pk=self.kwargs['pk']).team.id
        context["team"] = Teams.objects.get(pk=team_id)
        return context
    
    def form_valid(self, form):
        form.save()
        return super().form_valid(form)

    def get_success_url(self):
        team_id = Players.objects.get(pk=self.kwargs['pk']).team.id
        return reverse_lazy('team_detail', kwargs={'pk': team_id})

错误原因

  1. 调用super().save()后,Django会自动关闭上传文件的文件句柄,此时再通过self.player_image这个FieldFile对象打开文件,会触发句柄已关闭的错误。
  2. 即使未上传新图片,直接操作FieldFile对象也可能因内部状态问题导致报错。
  3. 手动调用image.close()干扰了Django对文件句柄的管理。

解决方案

1. 修改模型save方法,仅处理新上传的图片并使用文件路径

调整save方法逻辑,仅在有新图片上传时执行缩放,且通过文件路径而非FieldFile对象操作:

def save(self, *args, **kwargs):
    # 标记是否有新上传的图片:存在图片且未提交到数据库
    need_resize = self.player_image and not self.player_image._committed
    # 先保存模型到数据库
    super().save(*args, **kwargs)
    # 仅当有新图片时执行缩放,用文件路径打开避免句柄问题
    if need_resize:
        image_resize(self.player_image.path, 250)

2. 重写图片缩放函数,直接操作文件路径

更新缩放函数,接收文件路径作为参数,使用with语句自动管理图片资源:

from PIL import Image

def image_resize(image_path, tgt_width):
    with Image.open(image_path) as img:
        width, height = img.size
        target_width = tgt_width
        h_coefficient = width / tgt_width
        target_height = height / h_coefficient
        # 用LANCZOS替代已弃用的ANTIALIAS
        img = img.resize((int(target_width), int(target_height)), Image.LANCZOS)
        img.save(image_path, quality=100)

3. 优化视图中的重复数据库查询

避免重复查询数据库,直接从已获取的self.object中获取关联数据:

@method_decorator(superuser_required, name='dispatch')
class PlayerUpdateView(UpdateView):
    model = Players
    template_name = 'scoreboard/players/player_update.html'
    form_class = PlayersForm
    context_object_name: str = 'player'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # 直接从当前对象取team,无需重复查询
        context["team"] = self.object.team
        return context
    
    def get_success_url(self):
        # 直接使用当前对象的team id
        return reverse_lazy('team_detail', kwargs={'pk': self.object.team.id})

内容的提问来源于stack exchange,提问作者Victor Donoso

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 14:20:34