flask-restx icon indicating copy to clipboard operation
flask-restx copied to clipboard

Upload list of files using restX + Swagger

Open maory69 opened this issue 3 years ago • 14 comments

Hey,

I'm trying to upload a list of files to my post method using the swaggerUI I am defining my input using RequestParser and add_argument method. Uploading one file works just fine, but once I'm adding 'action=append' to make it a list of files it doesn't work.

my code looks like this:

from flask_restx import Namespace, Resource, reqparse
from werkzeug.datastructures import FileStorage
upload_parser = reqparse.RequestParser()
upload_parser.add_argument('images', location='files',
                           type=FileStorage, required=True, action="append")


@api.route("/")
class MyResource(Resource):
    @api.expect(upload_parser)
    def post(self):
        args = upload_parser.parse_args()
        images = args['images']

This is the cUrl from swagger: curl -X POST "http://127.0.0.1:8000/" -H "accept: application/json" -H "Content-Type: application/x-www-form-urlencoded" -d "images=%5Bobject%20File%5D&images=%5Bobject%20File%5D"

And the problem is that swagger sends it as application/x-www-form-urlencoded and not multipart/form-data Saying again, if I don't add the action='append' to the add_argument method, and I only send one file using Swagger it sends it as multipart/form-data

Is there any way to control it?

Screen Shot 2020-07-20 at 19 25 22 Screen Shot 2020-07-20 at 19 25 33 Screen Shot 2020-07-20 at 19 25 40

maory69 avatar Jul 20 '20 16:07 maory69

Im also having this issue. It appears to be similar to this: https://github.com/noirbizarre/flask-restplus/issues/463 Quite annoying...

datavistics avatar Jul 22 '20 11:07 datavistics

I tried forcing the content type with the decorator and the argument location as 'headers' as in the documentation but had the same issues as this issue: https://github.com/python-restx/flask-restx/issues/67

datavistics avatar Jul 23 '20 05:07 datavistics

I Tried the same code and uploaded multiple files got to knew that if upload_parser.add_argument('images', location='files', type=FileStorage, required=True, action="append")

  1. If i use required=True then i am getting below error { "errors": { "file": "Missing required parameter in an uploaded file" }, "message": "Input payload validation failed" }
  2. If i remove the require=True, then i am getting None on the args class Upload(Resource): def post(self): args = upload_parser.parse_args() print(args, '&&&&&&&&') file = args.get('file') print(file.filename) return "Uploaded file is " + file.filename

output{'file': None} &&&&&&&& AttributeError: 'NoneType' object has no attribute 'filename'

Could anyone help me on this while uploading multiple files how to get the filenames and swagger documentation for that as well

subu604 avatar Jan 10 '21 08:01 subu604

I am also having the same problem... Was this never solved?

ltpitt avatar Mar 24 '21 16:03 ltpitt

I think the problem is coming from the line https://github.com/python-restx/flask-restx/blob/af807aae3e18f70fc39a066d37f519cf32167efc/flask_restx/swagger.py#L469

It works when I replaced it by

if any(p["type"] == "file" or (p["type"] == "array" and p["items"]["type"]=="file") for p in all_params):

pletessier avatar Jun 23 '21 15:06 pletessier

This actually does not seem to be supported in Swagger UI currently. See Swagger Docs and GitHub Issue. But it seems that it still works when submitting multiple files via other means, such as Postman. This is even without this patch.

philnagel avatar Aug 16 '21 18:08 philnagel

Well, I do use Swagger UI to test my API, and my "patch" allows sending multiple files. screenshot-trombinos-dev priv ina-2021 08 17-17_21_46

Maybe it does not work in all contexts...

pletessier avatar Aug 17 '21 15:08 pletessier

I think the problem is coming from the line

https://github.com/python-restx/flask-restx/blob/af807aae3e18f70fc39a066d37f519cf32167efc/flask_restx/swagger.py#L469

It works when I replaced it by

if any(p["type"] == "file" or (p["type"] == "array" and p["items"]["type"]=="file") for p in all_params):

This solves the issue for me.

ClimenteA avatar Oct 06 '21 03:10 ClimenteA

While it makes the Swagger UI look correct, it does not actually submit the files in the correct way though. This is what it looks like recorded with Fiddler - just getting empty objects, and 2 files here are sent in the same key: image

And this is what it should look like, files sent as file objects with the correct Content-Type. Maybe the issue is just the missing Content-Type? image

When sent from a client other than swagger, the files do come through properly.

And also, when removing the action='append' arg, the single file is sent correctly as well: image

philnagel avatar Oct 21 '21 15:10 philnagel

Indeed, not sure why it worked for a while... Closed the pull request the solution doesn't work.

Used for tests: main.py.txt

ClimenteA avatar Oct 27 '21 07:10 ClimenteA

Any updates on this issue?

mdylan2 avatar May 02 '22 21:05 mdylan2

PR is still not approved. So you have to fork and merge for yourself

wernersbacher avatar Nov 09 '22 14:11 wernersbacher

Thank you!

mdylan2 avatar Nov 11 '22 22:11 mdylan2

https://github.com/python-restx/flask-restx/pull/542 PR in for this issue, takes @jingapore solution slightly further resolving issues with list comprehensions for keys that do not always exist in the dict & corresponding unit test.

TraivsBrookes96 avatar May 11 '23 18:05 TraivsBrookes96