Django REST Framework 上传图片:“提交的数据不是文件" [英] Django REST Framework upload image: "The submitted data was not a file"
问题描述
我正在学习如何在 Django 中上传文件,在这里我遇到了一个应该是微不足道的问题,错误是:
<块引用>提交的数据不是文件.检查表单上的编码类型.
下面是细节.
<小时>注意:我还查看了 Django Rest Framework ImageField,我试过
serializer = ImageSerializer(data=request.data, files=request.FILES)
但我明白
<块引用>TypeError: __init__()
得到一个意外的关键字参数 'files'
<小时>
我有一个 Image
模型,我想通过 Django REST 框架与之交互:
models.py
class Image(models.Model):image = models.ImageField(upload_to='item_images')owner = models.ForeignKey(用户,related_name='uploaded_item_images',空白=假,)time_created = models.DateTimeField(auto_now_add=True)
serializers.py
class ImageSerializer(serializers.ModelSerializer):图像 = 序列化器.ImageField(max_length=None, use_url=True,)元类:模型 = 图像fields = ("id", 'image', 'owner', 'time_created', )
settings.py
'DEFAULT_PARSER_CLASSES': ('rest_framework.parsers.JSONParser','rest_framework.parsers.FormParser','rest_framework.parsers.MultiPartParser',),
前端(使用 AngularJS 和 angular-restmod
或 $resource
)向 owner
发送 JSON
数据和 image
形式:
输入:
{"owner": 5, "image": "data:image/jpeg;base64,/9j/4QqdRXhpZgAATU0A..."}
在后端,request.data
显示
{u'owner': 5, u'image': u'data:image/jpeg;base64,/9j/4QqdRXhpZgAATU0AKgAAA..."}
但随后 ImageSerializer(data=request.data).errors
显示错误
ReturnDict([('image', [u'提交的数据不是文件.检查表单上的编码类型.'])])
我想知道我应该怎么做才能修复错误?
<小时>JS 部分
相关的前端代码由两部分组成:一个angular-file-dnd
指令
(可用这里) 将文件拖放到页面和 angular-restmod
,它提供 CRUD 操作:
<!-- 它将放置的图像存储到变量 $scope.image --><div file-dropzone="[image/png, image/jpeg, image/gif]" file="image" class='method' data-max-file-size="3" file-name="imageFileName"><div layout='row' layout-align='center'><i class="fa fa-upload" style='font-size:50px;'></i>
<div class='text-large'>Drap &把你的照片放在这里</div>
# 一个简单的 `Image` `model` 来执行 `POST`$scope.image_resource = Image.$build();$scope.upload = function() {console.log("上传");$scope.image_resource.image = $scope.image;$scope.image_resource.owner = Auth.get_profile().user_id;返回 $scope.image_resource.$save();};
<小时><小时>
关于这个问题的更新:现在我改用 ng-file-upload
,它以正确的格式发送图像数据.
您遇到的问题是 Django REST framework 期望通过标准文件上传方法将文件上传为多部分表单数据.这是通常是一个file
字段,但是 JavaScript Blob
对象也 适用于 AJAX.
您希望使用 base64 编码字符串而不是原始文件上传文件,原始文件 默认不支持.有 Base64ImageField
的实现,但最有前途的来自拉取请求.
由于这些主要是为 Django REST framework 2.x 设计的,我已经改进了 pull request 中的一个,并创建了一个应该与 DRF 3 兼容的.
serializers.py
from rest_framework 导入序列化程序类 Base64ImageField(serializers.ImageField):"一个 Django REST 框架字段,用于通过原始帖子数据处理图像上传.它使用 base64 对文件内容进行编码和解码.严重基于https://github.com/tomchristie/django-rest-framework/pull/1268针对 Django REST 框架 3 进行了更新."def to_internal_value(self, data):从 django.core.files.base 导入 ContentFile导入 base64进口六导入 uuid# 检查这是否是一个 base64 字符串if isinstance(data, Six.string_types):# 检查 base64 字符串是否在data:"中格式如果 'data:' 在数据中,';base64,' 在数据中:# 从 base64 内容中分离出标题header, data = data.split(';base64,')# 尝试解码文件.如果失败,则返回验证错误.尝试:decode_file = base64.b64decode(data)除了类型错误:self.fail('invalid_image')# 生成文件名:file_name = str(uuid.uuid4())[:12] # 12 个字符绰绰有余.# 获取文件扩展名:file_extension = self.get_file_extension(file_name, decoded_file)complete_file_name = "%s.%s";% (file_name, file_extension, )数据 = ContentFile(decoded_file, name=complete_file_name)返回 super(Base64ImageField, self).to_internal_value(data)def get_file_extension(self, file_name, decoded_file):导入图像扩展名 = imghdr.what(file_name, decoded_file)扩展名=jpg";如果扩展名==jpeg"其他扩展退货延期
这应该用于替换 Django REST 框架提供的标准 ImageField
.所以你的序列化器会变成
class ImageSerializer(serializers.ModelSerializer):图像 = Base64ImageField(max_length=None, use_url=True,)元类:模型 = 图像字段 = ("id", 'image', 'owner', 'time_created', )
这应该允许您指定 base64 编码的字符串,或 Django REST 框架通常期望的标准 Blob
对象.
I am leaning how to upload file in Django, and here I encounter a should-be-trivial problem, with the error:
The submitted data was not a file. Check the encoding type on the form.
Below is the detail.
Note: I also looked at Django Rest Framework ImageField, and I tried
serializer = ImageSerializer(data=request.data, files=request.FILES)
but I get
TypeError:
__init__()
got an unexpected keyword argument 'files'
I have a Image
model which I would like to interact with via Django REST framework:
models.py
class Image(models.Model):
image = models.ImageField(upload_to='item_images')
owner = models.ForeignKey(
User, related_name='uploaded_item_images',
blank=False,
)
time_created = models.DateTimeField(auto_now_add=True)
serializers.py
class ImageSerializer(serializers.ModelSerializer):
image = serializers.ImageField(
max_length=None, use_url=True,
)
class Meta:
model = Image
fields = ("id", 'image', 'owner', 'time_created', )
settings.py
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser',
),
The front end (using AngularJS and angular-restmod
or $resource
) send JSON
data with owner
and image
of the form:
Input:
{"owner": 5, "image": "data:image/jpeg;base64,/9j/4QqdRXhpZgAATU0A..."}
In the backend, request.data
shows
{u'owner': 5, u'image': u'data:image/jpeg;base64,/9j/4QqdRXhpZgAATU0AKgAAA..."}
But then ImageSerializer(data=request.data).errors
shows the error
ReturnDict([('image', [u'The submitted data was not a file. Check the encoding type on the form.'])])
I wonder what I should do to fix the error?
EDIT: JS part
The related front end codes consists of two parts: a angular-file-dnd
directive
(available here) to drop the file onto the page and angular-restmod
, which provides CRUD operations:
<!-- The template: according to angular-file-dnd, -->
<!-- it will store the dropped image into variable $scope.image -->
<div file-dropzone="[image/png, image/jpeg, image/gif]" file="image" class='method' data-max-file-size="3" file-name="imageFileName">
<div layout='row' layout-align='center'>
<i class="fa fa-upload" style='font-size:50px;'></i>
</div>
<div class='text-large'>Drap & drop your photo here</div>
</div>
# A simple `Image` `model` to perform `POST`
$scope.image_resource = Image.$build();
$scope.upload = function() {
console.log("uploading");
$scope.image_resource.image = $scope.image;
$scope.image_resource.owner = Auth.get_profile().user_id;
return $scope.image_resource.$save();
};
An update concerning the problem: right now I switched to using ng-file-upload
, which sends image data in proper format.
The problem that you are hitting is that Django REST framework expects files to be uploaded as multipart form data, through the standard file upload methods. This is typically a file
field, but the JavaScript Blob
object also works for AJAX.
You are looking to upload the files using a base64 encoded string, instead of the raw file, which is not supported by default. There are implementations of a Base64ImageField
out there, but the most promising one came by a pull request.
Since these were mostly designed for Django REST framework 2.x, I've improved upon the one from the pull request and created one that should be compatible with DRF 3.
serializers.py
from rest_framework import serializers
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):
from django.core.files.base import ContentFile
import base64
import six
import uuid
# 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
This should be used in replacement of the standard ImageField
provided by Django REST framework. So your serializer would become
class ImageSerializer(serializers.ModelSerializer):
image = Base64ImageField(
max_length=None, use_url=True,
)
class Meta:
model = Image
fields = ("id", 'image', 'owner', 'time_created', )
This should allow you to either specify a base64-encoded string, or the standard Blob
object that Django REST framework typically expects.
这篇关于Django REST Framework 上传图片:“提交的数据不是文件"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!