web-dev-qa-db-fra.com

FileUploadParser ne reçoit pas le nom du fichier

Je veux juste créer une API REST qui reçoit un fichier, le traite et renvoie des informations. Mon problème est que je suis cet exemple: http://www.Django-rest-framework.org/api-guide/parsers/#fileuploadparser

Et je ne peux pas le faire fonctionner avec Postman ou Curl, je pense que quelque chose me manque. L'analyseur me donne toujours ces deux erreurs:

  • Erreur d'analyse FileUpload - aucun des gestionnaires de téléchargement ne peut gérer le flux
  • Nom de fichier manquant. La demande doit inclure un en-tête Content-Disposition avec un paramètre filename. 

C'est le 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/'

Ici vous pouvez voir une capture de postier (j'ai tout essayé):

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--

exigences:

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

Merci beaucoup pour votre aide

6
Pablo Castilla

Dans Django REST framework. nous avons des composants comme les analyseurs syntaxiques, les moteurs de rendu et les sérialiseurs.

  • La responsabilité des analyseurs est d’analyser les données envoyées par les méthodes de requête GET, POST et PUT, etc.

  • L'analyseur par défaut utilisé dans Django REST est ' JSONParser '. Il analyse uniquement les données JSON de données [nombres, chaîne, date]. Il ignore les données comme les fichiers.

  • Pour analyser les fichiers, nous devons utiliser des analyseurs tels que " MultiPartParser " ou " FormParser ".

    Exemple de 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})
    

Lorsque nous utilisons la propriété request.data, l'analyseur analysera les données.

Références: Django REST Docs , Django REST Github

8
Anjaneyulu Batta

Je suis confronté au même problème. Le message d’erreur indique: 

{"detail": "Nom de fichier manquant. La requête doit inclure un en-tête Content-Disposition avec un paramètre de nom de fichier."} Je fais toutes les étapes au-dessus de ma réponse mais cela ne fonctionne pas.Enfin ,

Je trouve que la raison est dans le backend de viewset.  

ça se voit comme ça

parser_classes = (FileUploadParser, MultiPartParser, FormParser)

et je supprime le FileUploadParser

  parser_classes = ( MultiPartParser, FormParser)

et ça marche, alors je pense que vous devriez y prêter plus d'attention

1
Deft-pawN

dans votre views.py changer l'analyseur comme celui-ci

parser_classes = (JSONParser, MultiPartParser)

1
Darwin

Sur la deuxième erreur Missing filename. Request should include a Content-Disposition header with a filename parameter. utilisant postman, j'ai supprimé l'en-tête Content-Type :multipart/form-data et j'ai réussi.

Il semble que le problème est que l'en-tête Content-Type personnalisé remplace l'en-tête Content-Type par défaut à envoyer. Vérifiez ce fil pour référence Content-Type pour les messages en plusieurs parties

1
StackEdd

Vous n'avez pas besoin d'utiliser MultipartParser ou FormParser.

Ce dont vous avez besoin est un sérialiseur avec une FileField() comme ceci:

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)

Ainsi, lorsque vous essayez d'accéder à file ci-dessous, vous obtenez un dict avec une clé nommée file. Personnellement, je dirais probablement quelque chose de plus descriptif que simplement "fichier", mais cela dépend de vous.

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']
0
Paul Jurczyk