567

The XMLHttpRequest Level 2 standard (still a working draft) defines the FormData interface. This interface enables appending File objects to XHR-requests (Ajax-requests).

Btw, this is a new feature - in the past, the "hidden-iframe-trick" was used (read about that in my other question).

This is how it works (example):

var xhr = new XMLHttpRequest(),
    fd = new FormData();

fd.append( 'file', input.files[0] );
xhr.open( 'POST', 'http://example.com/script.php', true );
xhr.onreadystatechange = handler;
xhr.send( fd );

where input is a <input type="file"> field, and handler is the success-handler for the Ajax-request.

This works beautifully in all browsers (again, except IE).

Now, I would like to make this functionality work with jQuery. I tried this:

var fd = new FormData();    
fd.append( 'file', input.files[0] );

$.post( 'http://example.com/script.php', fd, handler );

Unfortunately, that won't work (an "Illegal invocation" error is thrown - screenshot is here). I assume jQuery expects a simple key-value object representing form-field-names / values, and the FormData instance that I'm passing in is apparently incompatible.

Now, since it is possible to pass a FormData instance into xhr.send(), I hope that it is also possible to make it work with jQuery.


Update:

I've created a "feature ticket" over at jQuery's Bug Tracker. It's here: http://bugs.jquery.com/ticket/9995

I was suggested to use an "Ajax prefilter"...


Update:

First, let me give a demo demonstrating what behavior I would like to achieve.

HTML:

<form>
    <input type="file" id="file" name="file">
    <input type="submit">
</form>

JavaScript:

$( 'form' ).submit(function ( e ) {
    var data, xhr;

    data = new FormData();
    data.append( 'file', $( '#file' )[0].files[0] );

    xhr = new XMLHttpRequest();

    xhr.open( 'POST', 'http://hacheck.tel.fer.hr/xml.pl', true );
    xhr.onreadystatechange = function ( response ) {};
    xhr.send( data );

    e.preventDefault();
});

The above code results in this HTTP-request:

multipartformdata

This is what I need - I want that "multipart/form-data" content-type!


The proposed solution would be like so:

$( 'form' ).submit(function ( e ) {
    var data;

    data = new FormData();
    data.append( 'file', $( '#file' )[0].files[0] );

    $.ajax({
        url: 'http://hacheck.tel.fer.hr/xml.pl',
        data: data,
        processData: false,
        type: 'POST',
        success: function ( data ) {
            alert( data );
        }
    });

    e.preventDefault();
});

However, this results in:

wrongcontenttype

As you can see, the content type is wrong...

Community
  • 1
  • 1
Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
  • 1
    YES! this is a great idea! thanks for the info as well. I recently found a snippet that does this but it's not jQuery and I was wondering if jQuery could do it. This is more of an answer than a question to me. Keep us posted! – Stephen Aug 23 '11 at 20:37
  • Now if only I/we knew what to put in said prefilter. – George R Sep 06 '11 at 05:07
  • 1
    Same situation here. Any more clues from anyone? – zaf Nov 23 '11 at 13:58
  • @zaf Setting `processData:false` is a step in the right direction (see pradeek's proposed solution). Now if I could manually set the "Content-Type" HTTP-request header... See my updated question for details. – Šime Vidas Nov 28 '11 at 16:52
  • 6
    isn't your question answered here? http://stackoverflow.com/questions/5392344/sending-multipart-formdata-with-jquery-ajax – Oleg Mikheev Nov 29 '11 at 09:24
  • @Oleg That could be the solution to my problem. Unfortunately, I cannot test this right now `:/` – Šime Vidas Nov 29 '11 at 15:48
  • Hi I have a similar problem, I am trying to do this, but the upload should go to facebook api page. I opened a question on this: http://stackoverflow.com/questions/19908342/how-to-upload-video-to-facebook-page-from-a-video-link-via-javascript Help is appreciated! – Arturo Nov 11 '13 at 14:09
  • add enctype: 'multipart/form-data' to $.ajax options – Anup Shetty May 17 '17 at 07:59
  • I think you cant do it in ajax to support all the browsers, I might say good to check this ajax uploader plugin to see how they have done it [http://valums.com/ajax-upload/](http://valums.com/ajax-upload/) – nivanka Nov 29 '11 at 09:18
  • You ca use e-form element from EHTML: https://github.com/Guseyn/EHTML – Guseyn Ismayylov Nov 27 '19 at 21:25
  • Please see this https://stackoverflow.com/a/69296641/4514094 – keivan kashani Sep 23 '21 at 08:37

8 Answers8

1013

I believe you could do it like this :

var fd = new FormData();    
fd.append( 'file', input.files[0] );

$.ajax({
  url: 'http://example.com/script.php',
  data: fd,
  processData: false,
  contentType: false,
  type: 'POST',
  success: function(data){
    alert(data);
  }
});

Notes:

  • Setting processData to false lets you prevent jQuery from automatically transforming the data into a query string. See the docs for more info.

  • Setting the contentType to false is imperative, since otherwise jQuery will set it incorrectly.

Mike
  • 14,010
  • 29
  • 101
  • 161
pradeek
  • 21,445
  • 2
  • 31
  • 32
  • Unfortunately, this results in the wrong "Content-Type" HTTP-request header being set. See my question for detailed explanation what I want to achieve and why your current solution doesn't satisfy. Maybe I can set the Content-Type manually? – Šime Vidas Nov 28 '11 at 16:50
  • 12
    Yes,I believe you could manually set the contentType to 'multipart/form-data' by adding a key-value pair in $.ajax argument. – pradeek Nov 29 '11 at 15:05
  • Hi @pradeek, we tried this solution here (http://stackoverflow.com/questions/10215425/upload-photo-with-facebook-graph-api-and-javascript-convert-canvas-image-to-mul), but it wasn't clear how to pass the image data in the body of the POST request. Any clues? Thanks! – Crashalot Apr 19 '12 at 01:18
  • @Crashalot Can you be clear as to what you are trying to do? This question involves uploading a file whereas the one you've linked to involves sending canvas data as an image to the server. – pradeek Apr 19 '12 at 03:25
  • @pradeek, sorry for the confusion. we're trying to send canvas data as an image to the server as multipart/form-data. the goal is to upload a photo directly to facebook, but facebook expects the image data, as multipart/form-data, in the body of a POST request. – Crashalot Apr 19 '12 at 04:09
  • @pradeek : what is 'input' here js object – Ankur Loriya May 01 '12 at 13:55
  • Not working with jquery 1.7.2, `/myurl?[object%20FormData]` :( – Samuel Katz Jul 19 '12 at 10:13
  • 2
    Tested in Firefox, and Chrome. This is the solution. – Šime Vidas Jan 11 '13 at 01:44
  • 2
    Setting processData and contentType to false fixed the issue for me. Thank you, that was unpleasant. – superluminary Nov 06 '13 at 16:58
  • @SalmanPK if you're getting that url, you are sending it as a GET, this will only work as a POST – ry4nolson Jul 09 '15 at 21:50
  • with the example provided, IE will send full path of the input file while Chrome and Firefox will send just the filename. To get just the filename you need to use this line: fd.append('file', input.files[0], input.files[0].name); – jomsk1e Jan 13 '21 at 01:19
  • alternatively, you could convert FromData to a plain object, expected "natively" by jQuery: `$.ajax({ ..., data: Object.fromEntries(fd.entries()) ...})` – pilat May 18 '21 at 09:50
  • fixed it with contentType: false, i had a typo on it, it was driving me crazy! thanks! – rashid Nov 17 '22 at 10:35
33

You can send the FormData object in ajax request using the following code,

$("form#formElement").submit(function(){
    var formData = new FormData($(this)[0]);
});

This is very similar to the accepted answer but an actual answer to the question topic. This will submit the form elements automatically in the FormData and you don't need to manually append the data to FormData variable.

The ajax method looks like this,

$("form#formElement").submit(function(){
    var formData = new FormData($(this)[0]);
    //append some non-form data also
    formData.append('other_data',$("#someInputData").val());
    $.ajax({
        type: "POST",
        url: postDataUrl,
        data: formData,
        processData: false,
        contentType: false,
        dataType: "json",
        success: function(data, textStatus, jqXHR) {
           //process data
        },
        error: function(data, textStatus, jqXHR) {
           //process error msg
        },
});

You can also manually pass the form element inside the FormData object as a parameter like this

var formElem = $("#formId");
var formdata = new FormData(formElem[0]);

Hope it helps. ;)

Dan
  • 54
  • 1
  • 4
Lucky
  • 16,787
  • 19
  • 117
  • 151
  • 13
    Isn't `$(this)[0]` the same as `this` ? – dfl Aug 11 '16 at 15:53
  • Why $(this) returns an array? – halbano Jul 18 '17 at 00:49
  • 4
    @halbano `$(this)` returns not array, but jQuery collection object which can be iterated over just like array. `form[0]` does almost exactly the same as `form.first()`, which just returns the first, real HTML element. – SteveB Oct 26 '17 at 12:48
31

There are a few yet to be mentioned techniques available for you. Start with setting the contentType property in your ajax params.

Building on pradeek's example:

$('form').submit(function (e) {
    var data;

    data = new FormData();
    data.append('file', $('#file')[0].files[0]);

    $.ajax({
        url: 'http://hacheck.tel.fer.hr/xml.pl',
        data: data,
        processData: false,
        type: 'POST',

        // This will override the content type header, 
        // regardless of whether content is actually sent.
        // Defaults to 'application/x-www-form-urlencoded'
        contentType: 'multipart/form-data', 

        //Before 1.5.1 you had to do this:
        beforeSend: function (x) {
            if (x && x.overrideMimeType) {
                x.overrideMimeType("multipart/form-data");
            }
        },
        // Now you should be able to do this:
        mimeType: 'multipart/form-data',    //Property added in 1.5.1

        success: function (data) {
            alert(data);
        }
    });

    e.preventDefault();
});

In some cases when forcing jQuery ajax to do non-expected things, the beforeSend event is a great place to do it. For a while people were using beforeSend to override the mimeType before that was added into jQuery in 1.5.1. You should be able to modify just about anything on the jqXHR object in the before send event.

BenSwayne
  • 16,810
  • 3
  • 58
  • 75
  • 5
    There is a problem with this: [the spec](http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html) requires that a multipart content-type includes the boundary parameter (specifically, it’s stated as a *required* parameter). – Roman Starkov Feb 29 '12 at 19:58
  • 1
    @romkyns I was coding this from the top of my head. If you have some experience/knowledge that I don't, please go ahead and edit the answer! Now that you mentioned it, a quick search turned up this: http://stackoverflow.com/questions/5933949/how-to-send-multipart-form-data-form-content-by-ajax-no-jquery which should get you going in the right direction. (You could take from that example and incorporate it into the above jQuery if you are into that.) – BenSwayne Mar 01 '12 at 23:43
  • Actually the currently accepted answer (that I edited to add a crucial fix) does the right thing, at last. This was tricky :) – Roman Starkov Mar 02 '12 at 14:44
  • I had high hopes for this solution, but my $_FILES is still NULL... – socca1157 Aug 27 '14 at 18:58
7

I do it like this and it's work for me, I hope this will help :)

   <div id="data">
        <form>
            <input type="file" name="userfile" id="userfile" size="20" />
            <br /><br />
            <input type="button" id="upload" value="upload" />
        </form>
    </div>
  <script>
        $(document).ready(function(){
                $('#upload').click(function(){

                    console.log('upload button clicked!')
                    var fd = new FormData();    
                    fd.append( 'userfile', $('#userfile')[0].files[0]);

                    $.ajax({
                      url: 'upload/do_upload',
                      data: fd,
                      processData: false,
                      contentType: false,
                      type: 'POST',
                      success: function(data){
                        console.log('upload success!')
                        $('#data').empty();
                        $('#data').append(data);

                      }
                    });
                });
        });
    </script>   
talalalshehri
  • 135
  • 3
  • 12
5

JavaScript:

function submitForm() {
    var data1 = new FormData($('input[name^="file"]'));
    $.each($('input[name^="file"]')[0].files, function(i, file) {
        data1.append(i, file);
    });

    $.ajax({
        url: "<?php echo base_url() ?>employee/dashboard2/test2",
        type: "POST",
        data: data1,
        enctype: 'multipart/form-data',
        processData: false, // tell jQuery not to process the data
        contentType: false // tell jQuery not to set contentType
    }).done(function(data) {
        console.log("PHP Output:");
        console.log(data);
    });
    return false;
}

PHP:

public function upload_file() {
    foreach($_FILES as $key) {
        $name = time().$key['name'];
        $path = 'upload/'.$name;
        @move_uploaded_file($key['tmp_name'], $path);
    }
}
Mohammad
  • 21,175
  • 15
  • 55
  • 84
Rahul Yadav
  • 867
  • 11
  • 12
4

You can use the $.ajax beforeSend event to manipulate the header.

beforeSend: function(xhr) { 
    xhr.setRequestHeader('Content-Type', 'multipart/form-data');
}

See this link for additional information: http://msdn.microsoft.com/en-us/library/ms536752(v=vs.85).aspx

dmnkhhn
  • 1,013
  • 1
  • 10
  • 18
1

Instead of - fd.append( 'userfile', $('#userfile')[0].files[0]);

Use - fd.append( 'file', $('#userfile')[0].files[0]);

Amit Joki
  • 58,320
  • 7
  • 77
  • 95
1

If you want to submit files using ajax use "jquery.form.js" This submits all form elements easily.

Samples http://jquery.malsup.com/form/#ajaxSubmit

rough view :

<form id='AddPhotoForm' method='post' action='../photo/admin_save_photo.php' enctype='multipart/form-data'>


<script type="text/javascript">
function showResponseAfterAddPhoto(responseText, statusText)
{ 
    information= responseText;
    callAjaxtolist();
    $("#AddPhotoForm").resetForm();
    $("#photo_msg").html('<div class="album_msg">Photo uploaded Successfully...</div>');        
};

$(document).ready(function(){
    $('.add_new_photo_div').live('click',function(){
            var options = {success:showResponseAfterAddPhoto};  
            $("#AddPhotoForm").ajaxSubmit(options);
        });
});
</script>
Rohit
  • 165
  • 4