使用Django-resized上传图片时,如何避免SuspiciousFileOperation错误?
问题描述
我之前在模型里用save()方法实现图片缩放和哈希值生成,现在改用django-resized模块的ResizedImageField来处理缩放,模型代码如下:
from django_resized import ResizedImageField class UserProfilePhoto(Model): photo = ResizedImageField(size=[128, 128], upload_to=MEDIA_ROOT) photo_hash = BigIntegerField( blank=True, null=True, help_text=_("an integer representation of the hexdigest hash of the photo"), ) def __str__(self): return f"{self.photo.name} ({self.photo_hash})"
但在后台管理页面添加图片时,触发了如下错误:
SuspiciousFileOperation at /admin/userprofile/userprofilephoto/add/ Detected path traversal attempt in '/app/mine/media/mendlebrot-lawn.jpeg'
我不会换回ImageField,请问怎么解决这个错误?
解决方案
1. 修复upload_to参数的错误
出现这个错误的核心原因是ResizedImageField的upload_to传了绝对路径(MEDIA_ROOT),而Django的文件字段要求upload_to是相对于MEDIA_ROOT的相对路径,绝对路径会被判定为路径遍历风险。
修改代码,把upload_to改成相对路径:
photo = ResizedImageField(size=[128, 128], upload_to='profile_photos/')
这样上传的图片会自动存在MEDIA_ROOT/profile_photos/目录下,不会触发路径检测错误。
2. 补充图片哈希值的生成逻辑
既然用了django-resized自动处理缩放,你可以重写save()方法,在图片保存后计算处理后图片的哈希值:
import hashlib from django_resized import ResizedImageField from django.db import models from django.utils.translation import gettext_lazy as _ class UserProfilePhoto(models.Model): photo = ResizedImageField(size=[128, 128], upload_to='profile_photos/') photo_hash = models.BigIntegerField( blank=True, null=True, help_text=_("an integer representation of the hexdigest hash of the photo"), ) def save(self, *args, **kwargs): super().save(*args, **kwargs) # 只有当图片存在且哈希未生成时计算 if self.photo and not self.photo_hash: # 打开处理后的图片文件 with self.photo.open('rb') as f: # 计算MD5哈希(也可以用其他哈希算法) md5_hash = hashlib.md5() for chunk in iter(lambda: f.read(4096), b''): md5_hash.update(chunk) # 将十六进制哈希转为整数存入photo_hash self.photo_hash = int(md5_hash.hexdigest(), 16) # 仅更新photo_hash字段,避免递归保存 super().save(update_fields=['photo_hash'], *args, **kwargs) def __str__(self): return f"{self.photo.name} ({self.photo_hash})"
关于关闭验证的说明
不建议直接关闭Django的路径遍历验证,这会引入安全风险。如果确实有极端场景需要(不推荐),可以在项目的settings.py里添加:
# 不推荐!仅用于特殊极端场景 SUSPICIOUS_FILE_OPERATION_ENFORCE = False
但强烈建议用前面的方法修复参数错误,而非关闭安全验证。
内容的提问来源于stack exchange,提问作者Back2Basics
相关产品推荐
相关产品推荐

