问题
I just want to create a REST API that receives a file, process it and return some information. My problem is that I am following this example: http://www.django-rest-framework.org/api-guide/parsers/#fileuploadparser
And I can't make it work using Postman or curl, I think I am missing something. The parser always gives me these two errors:
- FileUpload parse error - none of upload handlers can handle the stream
- Missing filename. Request should include a Content-Disposition header with a filename parameter.
This is the code:
views.py:
class FileUploadView(APIView):
parser_classes = (FileUploadParser,)
def post(self, request, filename, format=None):
file_obj = request.data['file']
# ...
# do some stuff with uploaded file
# ...
return Response(status=204)
def put(self, request, filename, format=None):
file_obj = request.data['file']
# ...
# do some stuff with uploaded file
# ...
return Response(status=204)
urls.py
urlpatterns = [
url(r'predict/(?P<filename>[^/]+)$', app.views.FileUploadView.as_view())
]
settings.py
"""
Django settings for GenderAPI project.
Generated by 'django-admin startproject' using Django 1.9.1.
For more information on this file, see
https://docs.djangoproject.com/en/1.9/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.9/ref/settings/
"""
import os
import posixpath
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': 'debug.log',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'DEBUG',
'propagate': True,
},
},
}
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = removed
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['localhost','127.0.0.1']
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.FileUploadParser'
)
}
# Application definition
INSTALLED_APPS = [
# Add your apps here to enable them
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'app'
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware'
]
ROOT_URLCONF = 'GenderAPI.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'GenderAPI.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = posixpath.join(*(BASE_DIR.split(os.path.sep) + ['static']))
FILE_UPLOAD_TEMP_DIR = BASE_DIR
MEDIA_URL = '/media/'
Here you can see a postman capture (I have tried everything):
PUT /predict/pabloGrande.jpg HTTP/1.1
Host: 127.0.0.1:52276
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="04320cf.jpg"
Content-Type: image/jpeg
------WebKitFormBoundary7MA4YWxkTrZu0gW--
requirements:
bleach==1.5.0
Django==1.11.6
djangorestframework==3.7.1
html5lib==0.9999999
Markdown==2.6.9
numpy==1.13.3
olefile==0.44
pandas==0.20.3
Pillow==4.3.0
pip==9.0.1
protobuf==3.4.0
python-dateutil==2.6.1
pytz==2017.2
scipy==1.0.0rc1
setuptools==28.8.0
six==1.11.0
tensorflow==1.3.0
tensorflow-tensorboard==0.1.8
Werkzeug==0.12.2
wheel==0.30.0
Thanks so much for your help
回答1:
In django REST framework. we have components like Parsers, Renderers and Serializers.
The responsibility of Parsers is to parse the data that is sent by request methods GET, POST and PUT, etc.
Default parser used in django REST is 'JSONParser'. It only parses the data JSON data[numbers, string, date]. It ignores the data like FILES.
In order to parse the FILES we need to use parsers like "MultiPartParser" or "FormParser".
Example Code :
from rest_framework.parsers import MultiPartParser from rest_framework.response import Response from rest_framework.views import APIView class ExampleView(APIView): """ A view that can accept POST requests with JSON content. """ parser_classes = (MultiPartParser,) def post(self, request, format=None): # to access files print request.FILES # to access data print request.data return Response({'received data': request.data})
When we use property request.data
then parser will parse the data.
References: Django REST Docs, Django REST Github
回答2:
I face the same problem.The problem error message shows:
{"detail":"Missing filename. Request should include a Content-Disposition header with a filename parameter."} I do all the steps above my answer but it doesn't work.Finally,
I find the reason is in the backend of viewset.
it shows like this
parser_classes = (FileUploadParser, MultiPartParser, FormParser)
and I remove the FileUploadParser
parser_classes = ( MultiPartParser, FormParser)
and It works, so I think you should pay more attention to it
回答3:
in your views.py change the parser like this
parser_classes = (JSONParser, MultiPartParser)
回答4:
On the second error Missing filename. Request should include a Content-Disposition header with a filename parameter.
using postman I removed the header Content-Type :multipart/form-data
and was successful.
It seems the issue is the custom Content-Type header overrides the default Content-Type header that should be sent. Check this thread for reference Content-Type for multipart posts
回答5:
You don't need to use MultipartParser
or FormParser
.
What you need is a serializer with a FileField()
like so:
serializers.py:
class FileUploadSerializer(serializers.Serializer):
# I set use_url to False so I don't need to pass file
# through the url itself - defaults to True if you need it
file = serializers.FileField(use_url=False)
So then when you try to access file
here below, you'll have a dict with a key named file
. Personally, I'd probably name it something more descriptive than just "file", but that's up to you.
from .serializers import FileUploadSerializer
class FileUploadView(APIView):
def post(self, request):
# set 'data' so that you can use 'is_vaid()' and raise exception
# if the file fails validation
serializer = FileUploadSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
# once validated, grab the file from the request itself
file = request.FILES['file']
来源:https://stackoverflow.com/questions/46806335/fileuploadparser-doesnt-get-the-file-name