Django使用post_save存储ImageField图片base64时报FileNotFound错误
问题诱因
- 路径调用错误:
instance.thumbnail.url返回的是面向HTTP访问的资源路径(比如报错信息里的/media/feeds/sisi_q1f0hdr.jpeg),不是本地文件系统的物理路径,open()方法只能读取本地路径的文件,所以直接触发文件不存在错误,需要改用instance.thumbnail.path获取文件在服务器上的真实存储路径。 - 字段名不匹配:模型里定义的base64存储字段是
img_b64,但信号代码里赋值的是instance.image_b64,字段名写错,就算路径正确也无法把值写入对应字段。 - 存在无限递归风险:post_save信号是实例保存完成后触发的,你在信号里又调用
instance.save(),会再次触发post_save信号,形成无限循环,最终会导致程序崩溃。
更优实现方案
推荐直接重写模型的save()方法实现需求,比信号逻辑更直观,也更容易避免递归问题,代码如下:
import base64 from django.db import models # 保留原有其他导入内容 class Feed(models.Model): # 原有模型字段全部保留不变 id = models.BigAutoField(primary_key=True) title = models.CharField(max_length = 200) content = models.TextField() thumbnail = models.ImageField(upload_to='feeds/', blank=True) img_b64 = models.BinaryField(blank=True, null=True) author = models.ForeignKey(Account, on_delete=models.CASCADE) date = models.DateTimeField(auto_now_add=True) view = models.IntegerField(default=0) kategori = models.CharField(max_length=100, default="None") def save(self, *args, **kwargs): # 先执行原生保存逻辑,确保缩略图已经写入磁盘 super().save(*args, **kwargs) # 仅当有缩略图且未生成过base64时处理,避免重复操作 if self.thumbnail and not self.img_b64: # 用path属性获取本地真实路径读取文件 with open(self.thumbnail.path, "rb") as img_file: self.img_b64 = base64.b64encode(img_file.read()) # 只更新base64字段,避免再次触发全量保存逻辑 super().save(update_fields=['img_b64'])
如果一定要用post_save信号实现,修改后的代码如下:
@receiver(post_save, sender=Feed) def save_base64(sender, instance, created, **kwargs): # 仅在实例首次创建、有缩略图、base64字段为空时处理 if created and instance.thumbnail and not instance.img_b64: with open(instance.thumbnail.path, "rb") as img_file: instance.img_b64 = base64.b64encode(img_file.read()) # 仅更新指定字段,避免触发post_save递归 instance.save(update_fields=['img_b64'])
注意:把图片base64存入数据库会大幅增加数据库体积,拖慢查询效率,非必要场景不推荐这么使用。如果是为了前端渲染直接返回base64,也可以在接口层动态读取图片生成base64返回,不用持久化存储到库中。
内容的提问来源于stack exchange,提问作者Andi Fathul Mukminin
相关产品推荐
相关产品推荐

