38

Hi I'm quite new to flask and I want to upload a file using an ajax call to the server. As mentioned in the documentation, I added a file upload to the html as folows:

<form action="" method=post enctype="multipart/form-data" id="testid">
 <table>
  <tr>
   <td>
     <label>Upload</label>
   </td>
   <td>
     <input id="upload_content_id" type="file" name="upload_file" multiple>
     <input type="button" name="btn_uplpad" id="btn_upload_id" class="btn-upload" value="Upload"/>

   </td>
  </tr>
 </table>
</form>

and I wrote the ajax handler as this

$(document).ready(function() {
    $("#btn_upload_id" ).click(function() {           
        $.ajax({
            type : "POST",
            url : "/uploadajax",
            cache: false,
            async: false,
            success : function (data) {},
            error: function (XMLHttpRequest, textStatus, errorThrown) {}
        });
    });
});

I do not know how to get the uploaded file (not the name) from this

  <input id="upload_content_id" type="file" name="upload_file" multiple>

and save the file in folder. I'm not quite sure how to read the file from handler which i have written:

@app.route('/uploadajax', methods = ['POST'])
def upldfile():
    if request.method == 'POST':
        file_val = request.files['file']

I will be grateful if anyone can help. Thank you in advance

Matthew Moisen
  • 16,701
  • 27
  • 128
  • 231
not 0x12
  • 19,360
  • 22
  • 67
  • 133
  • You can have a look at this post which suggests a flask-sijax to handle that http://stackoverflow.com/questions/14416706/upload-file-in-ajax-with-wtforms –  Aug 20 '13 at 12:21

3 Answers3

66

To answer your question...

HTML:

<form id="upload-file" method="post" enctype="multipart/form-data">
    <fieldset>
        <label for="file">Select a file</label>
        <input name="file" type="file">
    </fieldset>
    <fieldset>
        <button id="upload-file-btn" type="button">Upload</button>
    </fieldset>
</form>

JavaScript:

$(function() {
    $('#upload-file-btn').click(function() {
        var form_data = new FormData($('#upload-file')[0]);
        $.ajax({
            type: 'POST',
            url: '/uploadajax',
            data: form_data,
            contentType: false,
            cache: false,
            processData: false,
            success: function(data) {
                console.log('Success!');
            },
        });
    });
});

Now in your flask's endpoint view function, you can access the file's data via flask.request.files.

On a side note, forms are not tabular data, therefore they do not belong in a table. Instead, you should resort to an unordered list, or a definition list.

  • Can you explain the code: new FormData($('#upload-file')[0]); What does the "0" means? – nam Jan 12 '14 at 21:55
  • 2
    Use Firebug, or whichever developer tools you use to bring up you browser's console. Now, in your JavaScript file: console.log($("#upload-file")); - As you can see, it returns an object. Suffixing with "[0]" selects the first item in the object. FormData() creates a new FormData object, which is the object needed to send to your server. –  Mar 06 '14 at 19:44
  • I confirm that this should be the accepted answer. Works like charm! :) Thanks @onosendi! – swdev Mar 16 '14 at 22:29
  • 1
    As for your side note: unfortunatelly, given CSS current state (*), it is still an order of magnitude easier to have the input fields aligned one to another with a table than using other techniques. (*) It simply seens to be a missing use case on the CSS2 and 3 specs - -either that, or tables are indeed expected. – jsbueno Feb 27 '15 at 11:43
  • @onosendi tried to use your answer with some AJAX, do U know what I'm doing wrong here? http://stackoverflow.com/questions/36995516/getting-an-empty-immutablemultidict-object-from-jquery-request-data Thank you in advance! – mongotop May 06 '16 at 04:01
  • enctype="multipart/form-data" in the form is very important! – Dror Hilman Nov 28 '16 at 11:33
  • I get a deprecation warning when running this on Chrome: `XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/`. Changing `async` to `true` fixes it. – Gustavo Bezerra Oct 16 '17 at 08:00
  • @GustavoBezerra You can remove async:false in order to get rid of that error. See https://stackoverflow.com/questions/27736186/jquery-has-deprecated-synchronous-xmlhttprequest – zwep Apr 04 '18 at 10:39
  • Removed `async: false` from my answer. –  Aug 21 '18 at 04:55
7

JavaScript (client side):

var form_data = new FormData();
form_data.append('file', $('#uploadfile').prop('files')[0]);

$.ajax({
    type: 'POST',
    url: '/uploadLabel',
    data: form_data,
    contentType: false,
    cache: false,
    processData: false,
    success: function(data) {
        console.log('Success!');
    },
});

Python (server side):

@app.route('/uploadLabel', methods=['GET', 'POST'])
def uploadLabel():
    isthisFile = request.files.get('file')
    print(isthisFile)
    print(isthisFile.filename)
    isthisFile.save('./' + isthisFile.filename)
unknown6656
  • 2,765
  • 2
  • 36
  • 52
蘇韋文
  • 111
  • 1
  • 6
0

I faced a problem where the saved file was being empty, it turned out to be because of the pointer. Since I could not find anyone that mentioned this, here is a suggestion:

Files in a FormData request can be accessed at request.files then you can select the file you included in the FormData e.g. request.files['audio'].

So now if you want to access the actual bytes of the file, in our case 'audio' using .stream, you should make sure first that your cursor points to the first byte and not to the end of the file, in which case you will get empty bytes.

Hence, a good way to do it:

file = request.files['audio']
file.stream.seek(0)
audio = file.read()
Bourhano
  • 63
  • 6