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

上传时更新Django FileField文件内容引发I/O错误

Django上传文件加密时出现"I/O operation on closed file"的解决方案

问题原因

你遇到的错误是因为Django的UploadedFile(上传时的临时文件对象)不支持写入模式重新打开:

  • 第一次以rb模式读取后,底层临时文件会被关闭;
  • UploadedFile的设计仅用于读取上传的原始数据,没有提供写入接口,强行以wb模式打开会触发已关闭文件的I/O错误。

解决方案(符合存储无关、无临时文件夹需求)

我们不需要修改原上传文件,而是直接将加密后的内容包装成Django兼容的ContentFile对象,替换app_file字段的值。这样既不用临时文件夹,也和存储后端(本地/S3等)无关。

修改后的代码:

from django.core.files.base import ContentFile
from cryptography.fernet import Fernet

class AppFile(models.Model):
    app_file = models.FileField(upload_to=upload_to, validators=[validate_file_size])
    encrypted_data_key = models.CharField(max_length=500, blank=True)

    def encrypt_file_content(self, data_key):
        cipher = Fernet(data_key)
        # 读取原始上传文件内容
        with self.app_file.open(mode='rb') as file:
            file_data = file.read()
        # 加密内容
        encrypted_data = cipher.encrypt(file_data)
        # 用加密后的内容创建新文件,替换原字段值
        # 保留原文件名,若需标识加密文件可修改文件名(如加.encrypted后缀)
        self.app_file.save(
            self.app_file.name,
            ContentFile(encrypted_data),
            save=False  # 不立即保存到数据库,等待统一提交
        )

    def save(self, *args, **kwargs):
        if self._state.adding is True:
            # 从密钥管理服务生成密钥
            encrypted_data_key, data_key = self.generate_data_key_from_vault()
            self.encrypted_data_key = encrypted_data_key

            # 加密文件内容并替换字段
            self.encrypt_file_content(data_key)

        # 调用父类方法完成最终保存
        super().save(*args, **kwargs)

关键说明

  1. ContentFile的作用:将加密后的字节数据包装成Django能识别的文件对象,无需创建物理临时文件。
  2. save=False参数:避免单独保存文件字段,确保和模型实例的保存操作原子性。
  3. 存储无关性:不管你用本地存储还是云存储(如AWS S3),Django的FileField会自动处理加密文件的存储逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 09:08:10