Python Django REST Framework 上传图片:“提交的数据不是文件”
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28036404/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Django REST Framework upload image: "The submitted data was not a file"
提问by Lelouch
I am leaning how to upload file in Django, and here I encounter a should-be-trivial problem, with the error:
我正在学习如何在 Django 中上传文件,在这里我遇到了一个应该是微不足道的问题,错误是:
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
注意:我也看了Django Rest Framework ImageField,我试过了
serializer = ImageSerializer(data=request.data, files=request.FILES)
but I get
但我明白了
TypeError:
__init__()
got an unexpected keyword argument 'files'
类型错误:
__init__()
有一个意外的关键字参数“文件”
I have a Image
model which I would like to interact with via Django REST framework:
我有一个Image
模型,我想通过 Django REST 框架与之交互:
models.py
模型.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
序列化程序.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
设置.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:
前端(使用 AngularJS 和angular-restmod
或$resource
)JSON
以owner
和image
的形式发送数据:
Input:
输入:
{"owner": 5, "image": "data:image/jpeg;base64,/9j/4QqdRXhpZgAATU0A..."}
In the backend, request.data
shows
在后台,request.data
显示
{u'owner': 5, u'image': u'data:image/jpeg;base64,/9j/4QqdRXhpZgAATU0AKgAAA..."}
But then ImageSerializer(data=request.data).errors
shows the error
但随后ImageSerializer(data=request.data).errors
显示错误
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
编辑:JS部分
The related front endcodes 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:
相关的前端代码由两部分组成:a angular-file-dnd
directive
(可在此处获得)将文件拖放到页面上angular-restmod
,以及提供 CRUD 操作:
<!-- 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.
关于该问题的更新:现在我切换到使用ng-file-upload
,它以正确的格式发送图像数据。
采纳答案by Kevin Brown
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 worksfor AJAX.
您遇到的问题是 Django REST 框架希望通过标准文件上传方法将文件作为多部分表单数据上传。这通常是一个file
field,但JavaScriptBlob
对象也适用于 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.
您希望使用 base64 编码字符串而不是原始文件上传文件,默认情况下不支持原始文件。还有的实现Base64ImageField
在那里,但最有前途的一个走过来拉请求。
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.
由于这些主要是为 Django REST framework 2.x 设计的,我已经改进了 pull request 中的一个,并创建了一个应该与 DRF 3 兼容的。
serializers.py
序列化程序.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
这应该用于替换ImageField
Django REST 框架提供的标准。所以你的序列化器会变成
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.
这应该允许您指定 base64 编码的字符串,或者Blob
Django REST 框架通常期望的标准对象。
回答by levi
I ran in the same problem few days ago. Here is my django rest framework view to handle file uploading
几天前我遇到了同样的问题。这是我处理文件上传的 django rest 框架视图
views.py
视图.py
class PhotoUploadView(APIView):
parser_classes = (FileUploadParser,)
def post(self, request):
user = self.request.user
if not user:
return Response(status=status.HTTP_403_FORBIDDEN)
profile = None
data = None
photo = None
file_form = FileUploadForm(request.POST,request.FILES)
if file_form.is_valid():
photo = request.FILES['file']
else:
return Response(ajax_response(file_form),status=status.HTTP_406_NOT_ACCEPTABLE)
try:
profile = Organizer.objects.get(user=user)
profile.photo = photo
profile.save()
data = OrganizersSerializer(profile).data
except Organizer.DoesNotExist:
profile = Student.objects.get(user=user)
profile.photo = photo
profile.save()
data = StudentsSerializer(profile).data
return Response(data)
In front-end, I used angular-file-uploadlib.
在前端,我使用了angular-file-upload库。
Here is my file input
这是我的文件输入
<div ng-file-drop="" ng-file-select="" ng-model="organizer.photo" class="drop-box" drag-over-class="{accept:'dragover', reject:'dragover-err', delay:100}" ng-multiple="false" allow-dir="true" accept="image/*">
Drop Images or PDFs<div>here</div>
</div>
And here is my upload service
这是我的上传服务
main.js
主文件
(function () {
'use strict';
angular
.module('trulii.utils.services')
.factory('UploadFile', UploadFile);
UploadFile.$inject = ['$cookies', '$http','$upload','$window','Authentication'];
/**
* @namespace Authentication
* @returns {Factory}
*/
function UploadFile($cookies, $http,$upload,$window,Authentication) {
/**
* @name UploadFile
* @desc The Factory to be returned
*/
var UploadFile = {
upload_file: upload_file,
};
return UploadFile;
function upload_file(file) {
return $upload.upload({
url: '/api/users/upload/photo/', // upload.php script, node.js route, or servlet url
//method: 'POST' or 'PUT',
//headers: {'Authorization': 'xxx'}, // only for html5
//withCredentials: true,
file: file, // single file or a list of files. list is only for html5
//fileName: 'doc.jpg' or ['1.jpg', '2.jpg', ...] // to modify the name of the file(s)
//fileFormDataName: myFile, // file formData name ('Content-Disposition'), server side request form name
// could be a list of names for multiple files (html5). Default is 'file'
//formDataAppender: function(formData, key, val){} // customize how data is added to the formData.
// See #40#issuecomment-28612000 for sample code
})
}
}
})();