Django REST Framework 上传图片:“提交的数据不是文件" [英] Django REST Framework upload image: "The submitted data was not a file"

查看:48
本文介绍了Django REST Framework 上传图片:“提交的数据不是文件"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习如何在 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屋!

查看全文
相关文章
Python最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆