You need to enable JavaScript to run this app. Django 上传帖子图片触发缩放信号,文件存在却报FileNotFoundError如何解决
优惠活动
大模型
产品
解决方案
定价
更多

Django 上传帖子图片触发缩放信号,文件存在却报FileNotFoundError如何解决

FileNotFoundError 报错排查与修复

核心报错直接原因

代码读取原图片时传入了错误的路径:

  • 调用Image.open(img_dir)时的img_dir来自instance.media.url,这个属性返回的是面向前端页面的Web相对路径(例如/media/posts/123.jpg),并非服务器磁盘上的真实文件路径。PIL库操作本地文件时必须传入磁盘绝对路径,直接传入Web路径会找不到对应文件,触发FileNotFoundError。

现存代码全量问题列表

就算修正路径读取逻辑,原代码还存在以下会导致运行失败的问题:

  • 扩展名提取逻辑错误:原代码通过img_dir_list[-1].split(".")[0]提取后缀,实际取到的是去掉后缀的纯文件名,真正的文件扩展名是split后的最后一段内容
  • 未定义变量引用:resize逻辑中使用的image.width、image.height、img都没有提前定义,实际打开的图片对象名为im,运行时会抛出NameError
  • 文件保存路径逻辑错误:生成缩略图时用settings.MEDIA_URL拼接存储路径,MEDIA_URL是前端访问的路由前缀(例如默认值为/media/),拼接后得到的是系统根目录下的虚拟路径,和实际存储文件的MEDIA_ROOT磁盘路径完全不匹配,无法正常写入文件
  • 无意义的数据库写入:信号处理最后调用的instance.save()没有修改任何模型字段,会额外触发一次post_save信号流程,产生不必要的数据库IO
  • 模型定义语法错误:Post.media字段的upload_to="posts"后面多写了一个闭合右括号,会导致模型加载时报语法错误

完整修复方案

  1. 替换文件读取路径:使用Django文件字段自带的instance.media.path属性,该属性会自动根据配置的MEDIA_ROOT拼接出原图片在服务器磁盘上的绝对路径,可直接被PIL读取
  2. 修正路径拼接逻辑:生成缩略图时,基于原图片的磁盘路径推导存储目录,用MEDIA_ROOT维度的磁盘路径拼接保存地址,不要使用面向Web的MEDIA_URL
  3. 修正变量引用逻辑:统一使用已加载的原图片对象的宽高计算缩放比例,正确提取原文件的扩展名
  4. 删除多余的instance.save()调用:缩略图生成流程不涉及模型字段修改,不需要重新保存数据库记录
  5. 修正模型定义的语法错误,删掉多余的右括号

修复后的signals.py参考实现

import os
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from PIL import Image
from .models import Post

@receiver(post_save, sender=Post)
def generate_post_thumbnails(sender, instance, created, **kwargs):
    # 仅在新创建帖子时执行,避免更新操作重复生成缩略图
    if not created:
        return
    
    # 获取原图片在服务器磁盘的绝对路径
    original_img_disk_path = instance.media.path
    # 拆分原文件的基础名和真实扩展名
    original_base_name, file_ext = os.path.splitext(original_img_disk_path)
    # 缩略图和原图存放在同一目录下
    thumbnail_storage_dir = os.path.dirname(original_img_disk_path)

    original_img = Image.open(original_img_disk_path)
    # 依次生成200/400/600宽度的三个尺寸版本
    for target_width in [200, 400, 600]:
        target_height = int(target_width / original_img.width * original_img.height)
        resized_img = original_img.resize((target_width, target_height))
        # 拼接缩略图的完整磁盘保存路径
        thumbnail_disk_path = f"{thumbnail_storage_dir}/{os.path.basename(original_base_name)}_{target_width}x{target_height}{file_ext}"
        resized_img.save(thumbnail_disk_path)

修正后的Post模型定义

class Post(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    # 删除字段定义后多余的右括号
    media = models.ImageField(null=False, blank=False, upload_to="posts")
    caption = models.CharField(max_length=100, default="", null=True, blank=True)
    date = models.DateTimeField(default=timezone.now, null=True, blank=True)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.27 22:45:04