1

I run a Python Flask application and want to implement the possibility to upload files to the server. FileDrop.js looks promising for this task, however, I do not get it to work together with Flask.

The reason for this seems to be that Flask expects that the file is send to the server via POST along with an additional parameter that is used to identify the file from the application. I got this working using another file uploading framework, jQuery filedrop:

<html>
<head>
<script type="text/javascript" src="https://github.com/weixiyen/jquery-filedrop/blob/master/jquery.filedrop.js"></script>
</head>
<body>

<fieldset id="zone">
    <br><br>
    <legend><strong>Drop one or more files inside...</strong></legend>
    <br><br>
</fieldset> 

        <script type="text/JavaScript">

            // when the whole document has loaded, call the init function
            $(document).ready(init);

            function init() {

                var zone = $('#zone'),
                    message = $('.message', zone);

                // send all dropped files to /upload on the server via POST
                zone.filedrop({
                    paramname: 'file',
                    maxfiles: 200,
                    maxfilesize: 20,
                    url: '/upload',
                }

            }

        </script>

</body>
</html>

The paramname: 'file' is sent somehow with the request so that in my Flask application, I can get the uploaded file via:

@app.route('/upload', methods=['POST'])
def upload():

    if request.method == 'POST':
        file = request.files['file']
        file.save('myfile.ext')
        return 'Done'

However, how can I get my uploaded file using FileDrop.js? I don't see a possibility in the documentation how to pass an additional parameter via POST. When I follow the minimal examples from the documentation, for example

<html>
<head>
<script type="text/javascript" src="https://github.com/ProgerXP/FileDrop/blob/master/filedrop.js"></script>
</head>
<body>

    <fieldset id="zone">
      <legend>Drop a file inside...</legend>
      <p>Or click here to <em>Browse</em>...</p>
    </fieldset>

        <script type="text/JavaScript">

            // when the whole document has loaded, call the init function
            $(document).ready(init);

            function init() {

                var options = {iframe: {url: '/upload'}}
                var zone = new FileDrop('zone')

                // Do something when a user chooses or drops a file:
                zone.event('send', function (files) {

                // FileList might contain multiple items.
                files.each(function (file) {

                // Send the file:
                file.sendTo('/upload')

                  })
                })

            }

        </script>

</body>
</html>

then trying to inspect the uploaded file in Flask:

@app.route('/uploadtest', methods=['POST'])
def uploadtest():

    print(request.files)

    return 'end'

request.files is now a ImmutableMultiDict([]) and I don't know how to access it from Flask. Any suggestions?

Dirk
  • 9,381
  • 17
  • 70
  • 98

1 Answers1

2

I'm actually working on this same problem and can maybe help you with what I've determined.

First, the files attribute on the flask request is only when the data is sent from a form using a specific encoding and type. FileDrop doesn't use this so that files attribute in the request should be empty.

A tag is marked with enctype=multipart/form-data and an is placed in that form. http://flask.pocoo.org/docs/0.10/patterns/fileuploads/

FileDrop doesn't appear to send it that way as dropzone.js does using this form. What I did was look at the network request FileDrop makes. It's a post. I can see it is handled as a post. Where's the data? In the network request in my browser I can see a header called X-File-Name which is the url quoted name of the file being uploaded. I have access to that in the request object.

fileName = urlparse.unquote(request.headers['X-File-Name'])

Where's the actual data? It's in the body of the POST request. That's in the request.data--the entire file in whatever encoding format it was uploaded in.

foo = open('/tmp/%s' % fileName, 'w')
foo.write(request.data)
foo.close()

This is just an example, but it works. Obviously you should still follow the security tips on that flask "file uploads" page I linked for securing that file name, but otherwise that's all there is to it.

The immediate downside to using the post body is its one file per request, but that's not really a big deal for my particular application. Hope that helps.

voodoogiant
  • 2,118
  • 6
  • 29
  • 49
  • Thank you for this. Making the `files` attribute exist only when sent from a form in multi- mode, is the kind of unexpectedly specific functionality that drives me crazy. Or maybe I just don't know Flask well enough. Regardless, you've helped me immensely. – DaveTheScientist May 17 '16 at 16:00