noirbizarre / flask-restplus

Fully featured framework for fast, easy and documented API development with Flask
http://flask-restplus.readthedocs.org
Other
2.73k stars 506 forks source link

upload multiple files via swagger UI. #383

Open vvasuki opened 6 years ago

vvasuki commented 6 years ago

Given a parser like the below (from here):

@api.route('/dbs/<string:db_id>/books')
@api.param('db_id', 'Hint: Get one from the JSON object returned by another GET call. ')
class ImageBookList(BookList):

  post_parser = api.parser()
  # The below only results in the ability to upload a single file from the SwaggerUI. TODO: Surpass this limitation.
  post_parser.add_argument('in_files', type=FileStorage, location='files')
  post_parser.add_argument('book_json', location='form', type='string')

  @api.expect(post_parser, validate=True)
  def post(self, db_id):
    ...

the swagger UI (eg here) allows me to only upload a single file. How to get the swagger UI to allow uploading multiple files?

image

codethief commented 6 years ago

As a workaround, why don't you call the REST endpoint multiple times (once for each file)? Also, I don't understand your comment regarding the jsonStr parameter – I mean, obviously you can't accept form data and JSON at the same time: Either the POST request's bodys MIME type is application/json or it is multipart/form-data.

vvasuki commented 6 years ago

As a workaround, why don't you call the REST endpoint multiple times (once for each file)?

My service is to accept/ reject multiple files simultaneously - as an atomic transaction - so this wouldn't suffice.

Also, I don't understand your comment regarding the jsonStr parameter – I mean, obviously you can't accept form data and JSON at the same time: Either the POST request's bodys MIME type is application/json or it is multipart/form-data.

I suppose you were talking about the following commented code?

  # post_parser.add_argument('jsonStr', location='json') would lead to an error - "flask_restplus.errors.SpecsError: Can't use formData and body at the same time"

I just removed it from my initial post to avoid misunderstanding.

codethief commented 6 years ago

@vvasuki Yep, I was referring to the comment.

My service is to accept/ reject multiple files simultaneously - as an atomic transaction - so this wouldn't suffice.

I see, that makes sense. Having taken another look at your code, though, I'm wondering why you are only adding one argument to the parser that's of type=FileStorage. Why don't you add multiple ones (as would be required to upload multiple files as far as I understand the docs)?

Madhuv2 commented 5 years ago

Swagger UI is not supporting multiple uploads. Try it in post man.

BostonKenne commented 5 years ago

If you need to upload many files, try this:

import werkzeug
create_person = reqparse.RequestParser()
create_person.add_argument("images", 
                            type=werkzeug.datastructures.FileStorage,
                            location="files",
                            required=True,
                            help="Person images",
                            action='append')

It work for me 😍

rootVIII commented 4 years ago

The above solution is great until you need to add additional args (such as in the header). This is what I ended up using:

parser = reqparse.RequestParser()
parser.add_argument(
    'File1', type=FileStorage, location='files',
    help=parser_help1, required=True)
parser.add_argument(
    'File2', type=FileStorage, location='files',
    help=parser_help1, required=True)
parser.add_argument(
    'HeaderArg1', type=str, location='headers',
    help=parser_help2, required=True)
parser.add_argument(
    'HeaderArg2', type=str, location='headers',
    help=parser_help3, required=True)
TusharNimbhorkar commented 3 years ago

@BostonKenne @rootVIII How do you get the actual files from parser? args = self.parser.parse_args() doesnt work for me.

rootVIII commented 3 years ago

You might need to look up werkzeug file storage. You can just parse it in the view/route/endpoint via request.files. Might be good to look here: https://tedboy.github.io/flask/generated/generated/werkzeug.FileStorage.html and https://flask.palletsprojects.com/en/2.0.x/api/#flask.Request.files

You can actually parse the file content from the request without even having to save to disk if you don't want to.