Django REST Framework upload image: "The submitted data was not a file" Django REST Framework upload image: "The submitted data was not a file" angularjs angularjs

Django REST Framework upload image: "The submitted data was not a file"


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.


I ran in the same problem few days ago. Here is my django rest framework view to handle file uploading

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)

In front-end, I used angular-file-upload lib.

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