无法上传图像并在 Django 休息框架中获得预期的行为
Can't upload image and get expecteds behavior in django rest framework
过去两天我已经尝试了所有可能的选项,但似乎没有任何效果。请耐心等待,这会有点长。
这是我的 UserProfile 模型
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile', on_delete=models.CASCADE, )
status = models.CharField(max_length=255, null=True, blank=True)
thumbnail = models.ImageField(upload_to='media/user/', blank=True, null=True)
这是序列化程序,
class UserProfileSerializer(serializers.ModelSerializer):
thumbnail = serializers.ImageField(max_length=256, use_url=True, allow_empty_file=False)
class Meta:
model = models.UserProfile
fields = ('status', 'thumbnail',)
现在,当我尝试使用 POSTMAN 上传这张图片时,这是我得到的输出,
{
"thumbnail": [
"No file was submitted."
]
}
阅读一些 SO 帖子我尝试将默认的 serializer.ImageField 替换为这个,
class Base64ImageField(serializers.ImageField):
"""
A Django REST framework field for handling image-uploads through raw post data.
It uses base64 for encoding and decoding the contents of the file.
Heavily based on
https://github.com/tomchristie/django-rest-framework/pull/1268
Updated for Django REST framework 3.
"""
def to_internal_value(self, data):
# Check if this is a base64 string
if isinstance(data, six.string_types):
# Check if the base64 string is in the "data:" format
if 'data:' in data and ';base64,' in data:
# Break out the header from the base64 content
header, data = data.split(';base64,')
# Try to decode the file. Return validation error if it fails.
try:
decoded_file = base64.b64decode(data)
except TypeError:
self.fail('invalid_image')
# Generate file name:
file_name = str(uuid.uuid4())[:12] # 12 characters are more than enough.
# Get the file name extension:
file_extension = self.get_file_extension(file_name, decoded_file)
complete_file_name = "%s.%s" % (file_name, file_extension,)
data = ContentFile(decoded_file, name=complete_file_name)
return super(Base64ImageField, self).to_internal_value(data)
def get_file_extension(self, file_name, decoded_file):
import imghdr
extension = imghdr.what(file_name, decoded_file)
extension = "jpg" if extension == "jpeg" else extension
return extension
没有任何变化,我继续得到完全相同的行为。我期待的输出是这样的。
{
"status":"something",
"thumbnail":"file/path"
}
这就是我使用 POSTMAN 上传图片的方式。
有人可以帮我解决这个问题吗,我几乎已经尝试了 SO 上的每个选项都无济于事。
您应该通过邮递员发送 base64 编码的图像数据(而不是文件上传)(以防万一,您使用的是 Base64ImageField)
你也可以参考这个:
获取编码图像的过程
- 使用Image encoder进行图像编码。图片上传后,您可以选择复制图片。
- 复制数据并将其粘贴到 .txt 文件中
- 您将得到一个前缀为 'data:image/png;base64,'
的字符串
- 从字符串中删除该前缀并将整个字符串粘贴到 POSTMAN
我建议使用 multipart/form-data 将图像与其他 json 数据一起上传,如下所示:
# create a custom image field with additional constraints e.g. size of the image
class ConstrainedImageField(ImageField):
MAX_SIZE = 2000000 # in Bytes
default_error_messages = {
'image_size': _('The size of the image is {image_size} KB. The maximum size allowed is: {max_size} KB.'),
}
def to_internal_value(self, data):
super(ConstrainedImageField, self).to_internal_value(data=data)
file_size = data.size
if file_size > self.MAX_SIZE:
max_size_kb = self.MAX_SIZE/1000
file_size_kb = file_size/1000
self.fail('image_size', max_size=max_size_kb, image_size=file_size_kb)
class UserProfileSerializer(serializers.ModelSerializer):
thumbnail = serializers.ConstrainedImageField(max_length=256, use_url=True, allow_empty_file=False)
class Meta:
model = models.UserProfile
fields = ('status', 'thumbnail',)
def validate(self, data):
# ..
# ..
# get the image data from request.FILES:
self.context["thumbnail"] = self.context['request'].FILES.get("thumbnail")
return data
def create(self, validated_data):
# set the thumbnail field:
validated_data['thumbnail'] = self.context.get("thumbnail")
user_profile = UserProfile.objects.create(**validated_data)
return user_profile
过去两天我已经尝试了所有可能的选项,但似乎没有任何效果。请耐心等待,这会有点长。
这是我的 UserProfile 模型
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile', on_delete=models.CASCADE, )
status = models.CharField(max_length=255, null=True, blank=True)
thumbnail = models.ImageField(upload_to='media/user/', blank=True, null=True)
这是序列化程序,
class UserProfileSerializer(serializers.ModelSerializer):
thumbnail = serializers.ImageField(max_length=256, use_url=True, allow_empty_file=False)
class Meta:
model = models.UserProfile
fields = ('status', 'thumbnail',)
现在,当我尝试使用 POSTMAN 上传这张图片时,这是我得到的输出,
{
"thumbnail": [
"No file was submitted."
]
}
阅读一些 SO 帖子我尝试将默认的 serializer.ImageField 替换为这个,
class Base64ImageField(serializers.ImageField):
"""
A Django REST framework field for handling image-uploads through raw post data.
It uses base64 for encoding and decoding the contents of the file.
Heavily based on
https://github.com/tomchristie/django-rest-framework/pull/1268
Updated for Django REST framework 3.
"""
def to_internal_value(self, data):
# Check if this is a base64 string
if isinstance(data, six.string_types):
# Check if the base64 string is in the "data:" format
if 'data:' in data and ';base64,' in data:
# Break out the header from the base64 content
header, data = data.split(';base64,')
# Try to decode the file. Return validation error if it fails.
try:
decoded_file = base64.b64decode(data)
except TypeError:
self.fail('invalid_image')
# Generate file name:
file_name = str(uuid.uuid4())[:12] # 12 characters are more than enough.
# Get the file name extension:
file_extension = self.get_file_extension(file_name, decoded_file)
complete_file_name = "%s.%s" % (file_name, file_extension,)
data = ContentFile(decoded_file, name=complete_file_name)
return super(Base64ImageField, self).to_internal_value(data)
def get_file_extension(self, file_name, decoded_file):
import imghdr
extension = imghdr.what(file_name, decoded_file)
extension = "jpg" if extension == "jpeg" else extension
return extension
没有任何变化,我继续得到完全相同的行为。我期待的输出是这样的。
{
"status":"something",
"thumbnail":"file/path"
}
这就是我使用 POSTMAN 上传图片的方式。
有人可以帮我解决这个问题吗,我几乎已经尝试了 SO 上的每个选项都无济于事。
您应该通过邮递员发送 base64 编码的图像数据(而不是文件上传)(以防万一,您使用的是 Base64ImageField)
你也可以参考这个:
获取编码图像的过程
- 使用Image encoder进行图像编码。图片上传后,您可以选择复制图片。
- 复制数据并将其粘贴到 .txt 文件中
- 您将得到一个前缀为 'data:image/png;base64,' 的字符串
- 从字符串中删除该前缀并将整个字符串粘贴到 POSTMAN
我建议使用 multipart/form-data 将图像与其他 json 数据一起上传,如下所示:
# create a custom image field with additional constraints e.g. size of the image
class ConstrainedImageField(ImageField):
MAX_SIZE = 2000000 # in Bytes
default_error_messages = {
'image_size': _('The size of the image is {image_size} KB. The maximum size allowed is: {max_size} KB.'),
}
def to_internal_value(self, data):
super(ConstrainedImageField, self).to_internal_value(data=data)
file_size = data.size
if file_size > self.MAX_SIZE:
max_size_kb = self.MAX_SIZE/1000
file_size_kb = file_size/1000
self.fail('image_size', max_size=max_size_kb, image_size=file_size_kb)
class UserProfileSerializer(serializers.ModelSerializer):
thumbnail = serializers.ConstrainedImageField(max_length=256, use_url=True, allow_empty_file=False)
class Meta:
model = models.UserProfile
fields = ('status', 'thumbnail',)
def validate(self, data):
# ..
# ..
# get the image data from request.FILES:
self.context["thumbnail"] = self.context['request'].FILES.get("thumbnail")
return data
def create(self, validated_data):
# set the thumbnail field:
validated_data['thumbnail'] = self.context.get("thumbnail")
user_profile = UserProfile.objects.create(**validated_data)
return user_profile