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

Django模型save()读取上传文件报已关闭文件I/O操作错误

问题原因

当你在模型的save方法中调用self.save_file_content_to_attachment(self.file)时,此时Django尚未将上传的文件持久化到指定存储位置,且上传文件的流在请求处理阶段可能已被关闭,导致执行file.open('r')时触发"I/O operation on closed file"错误。

另外你的模型定义存在几个潜在问题:

  • UUIDField的默认值使用str(uuid4())会导致所有新创建实例共享同一个UUID(该表达式在模型加载时仅执行一次)
  • date和time字段的默认值使用datetime.datetime.now().date()/.time(),同样会导致所有新实例使用模型加载时的时间,而非创建实例的实时时间
解决方案

方案1:调整保存顺序,先持久化文件再读取内容

修改save方法,先调用父类save确保文件存入存储,再读取内容更新attachment字段,同时避免无限递归:

def save(self, *args, **kwargs):
    update_attachment = False
    if self.file and not self.pk:
        # 新建对象时先保存文件到存储
        super(CsvFile, self).save(*args, **kwargs)
        update_attachment = True
    
    if update_attachment:
        # 读取已存储的文件内容
        with self.file.open('r') as f:
            self.attachment = f.read()
        # 二次保存更新attachment字段
        super(CsvFile, self).save(*args, **kwargs)
    else:
        # 编辑对象或无文件上传时直接保存
        super(CsvFile, self).save(*args, **kwargs)
    
    self.original_filename = self.file.name if self.file else ''

方案2:直接读取上传文件的临时内容(更高效)

上传的文件在请求周期内可直接读取,无需调用open(),修改save_file_content_to_attachment方法:

def save_file_content_to_attachment(self, file):
    try:
        # 直接读取上传文件内容,根据实际编码调整decode参数
        self.attachment = file.read().decode('utf-8')
    except (FileNotFoundError, ValueError, UnicodeDecodeError):
        self.attachment = ''

def save(self, *args, **kwargs):
    self.original_filename = self.file.name if self.file else ''
    if self.file:
        # 判断是否为上传的临时文件(新建场景)
        if hasattr(self.file, 'temporary_file_path'):
            self.save_file_content_to_attachment(self.file)
        else:
            # 编辑场景下读取已存储的文件
            with self.file.open('r') as f:
                self.attachment = f.read()
    super(CsvFile, self).save(*args, **kwargs)
额外优化建议

修正模型字段的默认值问题:

class CsvFile(models.Model):
    processed = models.BooleanField(default=False)
    # 使用uuid4函数作为默认值,而非执行后的结果
    uid = models.UUIDField(unique=True, default=uuid4)
    # 使用lambda延迟执行,确保每次创建实例时获取当前时间
    date = models.DateField(null=False, default=lambda: datetime.datetime.now().date())
    time = models.TimeField(null=False, default=lambda: datetime.datetime.now().time())
    original_filename = models.CharField(max_length=600, blank=True)
    attachment = models.TextField(blank=True)
    file = models.FileField(upload_to="csv/", blank=True)
    
    # 其余方法保持修改后的版本

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 17:25:18