Django上传图片时同步生成50%比例缩略图的实现问题
Django单次上传同时保存原图和缩略图解决方案
问题核心
pre_save阶段原图还未写入磁盘,调用instance.link.path获取路径会报错- 直接在
post_save中调用save()会重复触发信号,导致无限循环保存
方案1:序列化器create方法中直接处理(最推荐)
将缩略图生成逻辑集中放在序列化器的create方法中,直接读取内存中的上传文件生成缩略图,无需依赖信号,一次数据库写入即可完成原图和缩略图的保存。
代码实现
首先导入依赖:
from PIL import Image from io import BytesIO from django.core.files import File
修改UserPhotoSerializer的create方法:
class UserPhotoSerializer(serializers.ModelSerializer): class Meta: model = UserPhoto fields = '__all__' def create(self, validated_data): # 提取上传的原图文件 origin_image = validated_data.get('link') if origin_image: # 直接从内存读取原图生成缩略图 img = Image.open(origin_image) width, height = img.size # 缩放为原图50%尺寸 resized_img = img.resize((int(width / 2), int(height / 2))).convert('RGB') blob = BytesIO() # 保存缩略图到内存,可自行调整quality参数控制画质 resized_img.save(blob, 'JPEG', quality=85) # 构造缩略图文件对象 mini_file = File(blob, name=f'mini_{origin_image.name}') validated_data['mini_link'] = mini_file validated_data["user"] = self.context['request'].user.User_userbase # 一次创建操作同时保存原图和缩略图 return UserPhoto.objects.create(**validated_data)
方案2:post_save信号处理(需避免无限循环)
如果需要保留信号的实现方式,可通过created判断+update_fields参数规避循环问题:
代码实现
修改信号逻辑:
@receiver(post_save, sender=UserPhoto) def create_mini_photo(sender, instance, created, **kwargs): # 仅首次创建实例且无缩略图时执行逻辑 if created and not instance.mini_link and instance.link: # 此时原图已落地磁盘,可正常读取路径 img = Image.open(instance.link.path) width, height = img.size resized_img = img.resize((int(width / 2), int(height / 2))).convert('RGB') blob = BytesIO() resized_img.save(blob, 'JPEG', quality=85) # save参数设为False,先不触发保存 instance.mini_link.save(f'mini_{instance.link.name}', File(blob), save=False) # 仅更新缩略图字段,避免全字段保存再次触发信号循环 instance.save(update_fields=['mini_link'])
注意事项
- 需提前安装Pillow依赖:
pip install pillow - 若需要支持PNG透明图,可将
convert('RGB')移除,保存格式改为PNG - 确保
ImageField配置的upload_to对应目录拥有写入权限
内容的提问来源于stack exchange,提问作者hasaneyldrm
相关产品推荐
相关产品推荐

