Django REST Framework上传图片:“提交的数据不是文件”

我正在学习如何在Django中上传文件,在这里我遇到了一个应该是微不足道的问题,错误:

提交的数据不是文件。 检查表单上的编码types。

以下是详细信息。


注:我也看着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( 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', ), 

前端(使用AngularJS和angular-restmod$resource )发送JSON数据的ownerimage的forms:

input:

 {"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'The submitted data was not a file. Check the encoding type on the form.'])]) 

我不知道该怎么办才能解决这个错误?


编辑:JS部分

相关的前端代码由两部分组成:一个用于将文件放到页面上的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(); }; 


关于这个问题的更新:现在我切换到使用ng-file-upload ,它以适当的格式发送图像数据。

您碰到的问题是,Django REST框架希望通过标准的file upload方法将文件作为多部分表单数据上传。 这通常是一个file字段 ,但JavaScript Blob对象也适用于AJAX。

您正在寻找使用base64编码的string上传文件,而不是默认情况下不支持的原始文件。 在那里有一个Base64ImageField实现 ,但是最有希望的是通过一个pull请求 。

由于这些主要是为Django REST framework 2.xdevise的,所以我对pull请求中的一个进行了改进,创build了一个与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 

这应该用于replaceDjango REST框架提供的标准ImageField 。 所以你的序列化程序将成为

 class ImageSerializer(serializers.ModelSerializer): image = Base64ImageField( max_length=None, use_url=True, ) class Meta: model = Image fields = ("id", 'image', 'owner', 'time_created', ) 

这应该允许你指定一个base64编码的string,或者Django REST框架通常期望的标准Blob对象。

我前几天跑了同样的问题。 这里是我的Djangorest框架视图来处理file upload

views.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) 

在前端,我使用了angular-file-upload lib。

这是我的文件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> 

这里是我的上传服务

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 }) } } })();