By zoku


2011-03-22 13:52:36 8 Comments

I've got a problem sending a file to a serverside PHP-script using jQuery's ajax-function. It's possible to get the File-List with $('#fileinput').attr('files') but how is it possible to send this Data to the server? The resulting array ($_POST) on the serverside php-script is 0 (NULL) when using the file-input.

I know it is possible (though I didn't find any jQuery solutions until now, only Prototye code (http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress.html)).

This seems to be relatively new, so please do not mention file upload would be impossible via XHR/Ajax, because it's definitely working.

I need the functionality in Safari 5, FF and Chrome would be nice but are not essential.

My code for now is:

$.ajax({
    url: 'php/upload.php',
    data: $('#file').attr('files'),
    cache: false,
    contentType: 'multipart/form-data',
    processData: false,
    type: 'POST',
    success: function(data){
        alert(data);
    }
});

11 comments

@szatti1489 2018-03-08 13:47:10

All the solutions above are looks good and elegant, but the FormData() object does not expect any parameter, but use append() after instantiate it, like what one wrote above:

formData.append(val.name, val.value);

@sudip 2018-02-08 10:42:19

Older versions of IE do not support FormData ( Full browser support list for FormData is here: https://developer.mozilla.org/en-US/docs/Web/API/FormData).

Either you can use a jquery plugin (For ex, http://malsup.com/jquery/form/#code-samples ) or, you can use IFrame based solution to post multipart form data through ajax: https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript

@Raphael Schweikert 2011-05-12 09:36:09

Starting with Safari 5/Firefox 4, it’s easiest to use the FormData class:

var data = new FormData();
jQuery.each(jQuery('#file')[0].files, function(i, file) {
    data.append('file-'+i, file);
});

So now you have a FormData object, ready to be sent along with the XMLHttpRequest.

jQuery.ajax({
    url: 'php/upload.php',
    data: data,
    cache: false,
    contentType: false,
    processData: false,
    method: 'POST',
    type: 'POST', // For jQuery < 1.9
    success: function(data){
        alert(data);
    }
});

It’s imperative that you set the contentType option to false, forcing jQuery not to add a Content-Type header for you, otherwise, the boundary string will be missing from it. Also, you must leave the processData flag set to false, otherwise, jQuery will try to convert your FormData into a string, which will fail.

You may now retrieve the file in PHP using:

$_FILES['file-0']

(There is only one file, file-0, unless you specified the multiple attribute on your file input, in which case, the numbers will increment with each file.)

Using the FormData emulation for older browsers

var opts = {
    url: 'php/upload.php',
    data: data,
    cache: false,
    contentType: false,
    processData: false,
    method: 'POST',
    type: 'POST', // For jQuery < 1.9
    success: function(data){
        alert(data);
    }
};
if(data.fake) {
    // Make sure no text encoding stuff is done by xhr
    opts.xhr = function() { var xhr = jQuery.ajaxSettings.xhr(); xhr.send = xhr.sendAsBinary; return xhr; }
    opts.contentType = "multipart/form-data; boundary="+data.boundary;
    opts.data = data.toString();
}
jQuery.ajax(opts);

Create FormData from an existing form

Instead of manually iterating the files, the FormData object can also be created with the contents of an existing form object:

var data = new FormData(jQuery('form')[0]);

Use a PHP native array instead of a counter

Just name your file elements the same and end the name in brackets:

jQuery.each(jQuery('#file')[0].files, function(i, file) {
    data.append('file[]', file);
});

$_FILES['file'] will then be an array containing the file upload fields for every file uploaded. I actually recommend this over my initial solution as it’s simpler to iterate over.

@Raphael Schweikert 2011-08-24 08:01:11

Also, there is a FormData emulation which will make porting this solution to older browsers quite simple. All you have to do is to check for data.fake and set the contentType property manually as well as overriding xhr to use sendAsBinary().

@Crashalot 2012-04-19 01:07:57

Hi @Raphael Schwekert, we tried this, but it didn't seem to work (stackoverflow.com/questions/10215425/…). Where do you set the boundary for the multipart/form-data?

@Raphael Schweikert 2012-04-19 06:27:32

Could you elaborate on “didn't seem to work”? What exactly have you tried? The FormData emulation or the native class? When using the emulation, the boundary is in data.boundary so you’d add xhr: function() { var xhr = jQuery.ajaxSettings.xhr(); xhr.send = xhr.sendAsBinary; return xhr; }, to the options and use "multipart/form-data; boundary="+data.boundary for the contentType option.

@Crashalot 2012-04-19 09:18:14

yes, sorry for the ambiguity. here's the code we tried: stackoverflow.com/questions/10220576/…

@Raphael Schweikert 2012-04-19 09:44:20

So you’re neither using jQuery nor the FormData class and you’re asking me in the context of a question specific to jQuery and an answer specific to using FormData? I’m sorry but I don’t think I can help you there…

@Crashalot 2012-04-19 18:50:32

Sorry @Raphael, that version didn't have our jQuery attempt coupled with FormData. We'll create a jsFiddle with that code. Sorry about that!

@Kim Stacks 2012-10-10 03:51:59

@RaphaelSchweikert I used your method successfully for /albums/add situation. I am now stuck in /albums/:id/edit situation. Could you give some advice? stackoverflow.com/questions/12811287/…

@albanx 2012-10-15 20:46:11

+1 This is the solution for that problem. But this will not work on Firefox 3.6 that do not support form data but supports ajax upload, even using the manual sending trick headers!!

@Timmmm 2013-04-06 20:43:25

This uses application/x-www-form-urlencoded. Is there a way to use multipart/form-data instead?

@Raphael Schweikert 2013-04-07 08:57:43

@Timmmm No, it uses multipart/form-data. Using application/x-www-form-urlencoded would not work.

@Royi Namir 2014-01-06 16:08:27

regarding contentType:false where is it in the api ? I dont see that it gets bool. ( dont get me wrong - it is working only when false - i just dont see where is it)

@Raphael Schweikert 2014-01-06 18:30:56

@RoyiNamir It’s only documented in code, I’m afraid.

@Jack 2014-05-19 21:40:38

Maybe the cacheoption should be omitted (see api.jquery.com/jquery.ajax): "Setting cache to false will only work correctly with HEAD and GET requests. It works by appending _={timestamp} to the GET parameters. The parameter is not needed for other types of requests, except in IE8 when a POST is made to a URL that has already been requested by a GET."

@Auspex 2014-05-28 14:07:46

@Jack More correctly, the cache option could be omitted. It shouldn't make any difference (except as noted for IE, where I have no idea if it helps or not).

@Frederic Yesid Peña Sánchez 2014-06-02 01:09:46

You solved my "ILLEGAL INVOKATION EXCEPTION" THANKS!!!

@Brownman Revival 2015-04-14 07:25:54

@RaphaelSchweikert if i echo $_FILES['file-0'] what should i expect to get as an ouput

@Raphael Schweikert 2015-04-14 07:31:25

@HogRider It’s an array with the following fields: name, type, tmp_name, error, size. See php.net/manual/en/features.file-upload.php

@Brownman Revival 2015-04-14 08:15:12

@RaphaelSchweikert stackoverflow.com/questions/29622293/… can you check it

@Raphael Schweikert 2015-04-15 15:25:21

@RoyiNamir: The official docs have since been amended: “As of jQuery 1.6 you can pass false to tell jQuery to not set any content type header.”

@Sharath Chandra 2015-10-13 17:46:55

@RaphaelSchweikert doesnt jQuery('#file')[0].files throw an error in older browsers like IE9 which does not support the files property?

@Raphael Schweikert 2015-10-13 17:53:37

@SharathChandra The question was specifically about how to post Files for Browsers that do support the File API. But no, no error is thrown, it just returns undefined. But yes, neither the native FormData solution nor the FormData emulation works on browsers that don’t support the File API. You’ll have to use other kinds of fallbacks for these browsers (iframe, Flash, ActiveX).

@Sharath Chandra 2015-10-13 17:55:15

@RaphaelSchweikert thanks for the clarification.

@Zero3 2015-12-19 19:08:12

@Timmmm maybe you forgot the contentType: false part.

@Hossein Margani 2016-01-20 10:31:29

Thank you for your complete answer. Why didn't anyone mention that the '[]' characters here -> "data.append('file[]', file);" is very important!!! I just saw it accidentally and it saved my day!

@Flaudre 2016-03-28 05:20:41

took me a moment, this is the selector for all file inputs: $("input[type='file']")[0].files

@Martin Becker 2016-11-03 16:40:08

If you need to set the content type of the form-data field, you can use the Blob class: data.append('file-1', new Blob([fileContent], { type: "application/json"}))

@Timothy Kovalev 2017-02-02 14:39:32

Oh, for god's sake, is this really happening? Brilliant API, jQuery!

@Kyborek 2017-12-08 15:07:26

According to caniuse.com/#search=FormData This solution has 94.7% global support over browsers (weighted by usage).

@ajmicek 2012-01-10 00:56:40

Just wanted to add a bit to Raphael's great answer. Here's how to get PHP to produce the same $_FILES, regardless of whether you use JavaScript to submit.

HTML form:

<form enctype="multipart/form-data" action="/test.php" 
method="post" class="putImages">
   <input name="media[]" type="file" multiple/>
   <input class="button" type="submit" alt="Upload" value="Upload" />
</form>

PHP produces this $_FILES, when submitted without JavaScript:

Array
(
    [media] => Array
        (
            [name] => Array
                (
                    [0] => Galata_Tower.jpg
                    [1] => 518f.jpg
                )

            [type] => Array
                (
                    [0] => image/jpeg
                    [1] => image/jpeg
                )

            [tmp_name] => Array
                (
                    [0] => /tmp/phpIQaOYo
                    [1] => /tmp/phpJQaOYo
                )

            [error] => Array
                (
                    [0] => 0
                    [1] => 0
                )

            [size] => Array
                (
                    [0] => 258004
                    [1] => 127884
                )

        )

)

If you do progressive enhancement, using Raphael's JS to submit the files...

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

$.ajax({
    type: ppiFormMethod,
    data: data,
    url: ppiFormActionURL,
    cache: false,
    contentType: false,
    processData: false,
    success: function(data){
        alert(data);
    }
});

... this is what PHP's $_FILES array looks like, after using that JavaScript to submit:

Array
(
    [0] => Array
        (
            [name] => Galata_Tower.jpg
            [type] => image/jpeg
            [tmp_name] => /tmp/phpAQaOYo
            [error] => 0
            [size] => 258004
        )

    [1] => Array
        (
            [name] => 518f.jpg
            [type] => image/jpeg
            [tmp_name] => /tmp/phpBQaOYo
            [error] => 0
            [size] => 127884
        )

)

That's a nice array, and actually what some people transform $_FILES into, but I find it's useful to work with the same $_FILES, regardless if JavaScript was used to submit. So, here are some minor changes to the JS:

// match anything not a [ or ]
regexp = /^[^[\]]+/;
var fileInput = $('.putImages input[type="file"]');
var fileInputName = regexp.exec( fileInput.attr('name') );

// make files available
var data = new FormData();
jQuery.each($(fileInput)[0].files, function(i, file) {
    data.append(fileInputName+'['+i+']', file);
});

(14 April 2017 edit: I removed the form element from the constructor of FormData() -- that fixed this code in Safari.)

That code does two things.

  1. Retrieves the input name attribute automatically, making the HTML more maintainable. Now, as long as form has the class putImages, everything else is taken care of automatically. That is, the input need not have any special name.
  2. The array format that normal HTML submits is recreated by the JavaScript in the data.append line. Note the brackets.

With these changes, submitting with JavaScript now produces precisely the same $_FILES array as submitting with simple HTML.

@medoingthings 2018-08-04 12:42:52

Had the same issue with Safari. Thanks for the hint!

@Karl Henselin 2016-06-15 16:54:52

Devin Venable's answer was close to what I wanted, but I wanted one that would work on multiple forms, and use the action already specified in the form so that each file would go to the right place.

I also wanted to use jQuery's on() method so I could avoid using .ready().

That got me to this: (replace formSelector with your jQuery selector)

$(document).on('submit', formSelecter, function( e ) {
        e.preventDefault();
    $.ajax( {
        url: $(this).attr('action'),
        type: 'POST',
        data: new FormData( this ),
        processData: false,
        contentType: false
    }).done(function( data ) {
        //do stuff with the data you got back.
    });

});

@james 2016-03-17 22:07:16

One gotcha I ran into today I think is worth pointing out related to this problem: if the url for the ajax call is redirected then the header for content-type: 'multipart/form-data' can be lost.

For example, I was posting to http://server.com/context?param=x

In the network tab of Chrome I saw the correct multipart header for this request but then a 302 redirect to http://server.com/context/?param=x (note the slash after context)

During the redirect the multipart header was lost. Ensure requests are not being redirected if these solutions are not working for you.

@Asad Malik 2014-08-30 06:49:40

Look at my code, it does the job for me

$( '#formId' )
  .submit( function( e ) {
    $.ajax( {
      url: 'FormSubmitUrl',
      type: 'POST',
      data: new FormData( this ),
      processData: false,
      contentType: false
    } );
    e.preventDefault();
  } );

@sergioBertolazzo 2017-07-30 22:37:43

thank you!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

@Devin Venable 2014-06-02 17:43:39

If your form is defined in your HTML, it is easier to pass the form into the constructor than it is to iterate and add images.

$('#my-form').submit( function(e) {
    e.preventDefault();

    var data = new FormData(this); // <-- 'this' is your form element

    $.ajax({
            url: '/my_URL/',
            data: data,
            cache: false,
            contentType: false,
            processData: false,
            type: 'POST',     
            success: function(data){
            ...

@sskoko 2016-07-14 06:36:51

This is the most elegant solution.

@user1909226 2013-07-18 09:54:58

  1. get form object by jquery-> $("#id")[0]
  2. data = new FormData($("#id")[0]);
  3. ok,data is your want

@Mohammad-Hossein Jamali 2015-05-29 06:47:16

$("#id")[0] returns first none empty <input type="file" /> of the form, how do you submit entire form including all <input type="file" /> of it?

@topkara 2013-02-22 00:24:13

The FormData class does work, however in iOS Safari (on the iPhone at least) I wasn't able to use Raphael Schweikert's solution as is.

Mozilla Dev has a nice page on manipulating FormData objects.

So, add an empty form somewhere in your page, specifying the enctype:

<form enctype="multipart/form-data" method="post" name="fileinfo" id="fileinfo"></form>

Then, create FormData object as:

var data = new FormData($("#fileinfo"));

and proceed as in Raphael's code.

@glyph 2013-03-01 21:17:55

I had a problem with my jquery ajax uploads silently hanging in Safari and ended up doing a browser conditional $('form-name').submit() for Safari instead of the ajax upload that works in IE9 and FF18. Probably not an ideal solution for multi-uploads but I was doing this for a single file into an iframe from a jquery dialog so it worked ok.

@evandro777 2012-09-14 14:33:12

I've just build this function based on some info I read.

Use it like using .serialize(), instead just put .serializefiles();.
Working here in my tests.

//USAGE: $("#form").serializefiles();
(function($) {
$.fn.serializefiles = function() {
    var obj = $(this);
    /* ADD FILE TO PARAM AJAX */
    var formData = new FormData();
    $.each($(obj).find("input[type='file']"), function(i, tag) {
        $.each($(tag)[0].files, function(i, file) {
            formData.append(tag.name, file);
        });
    });
    var params = $(obj).serializeArray();
    $.each(params, function (i, val) {
        formData.append(val.name, val.value);
    });
    return formData;
};
})(jQuery);

@Fallenreaper 2012-09-19 15:09:36

I was trying to get this working, but it seemed to not recognize serializefiles() as a function, despite this definition going at the top of the page.

@SchurigH 2013-11-19 21:53:24

that works for me just fine. getting data with var data = $("#avatar-form").serializefiles(); sending this via ajax data parameter and analysing with express formidable: form.parse(req, function(err, fields, files){ thank you for that code snippet :)

@sarath 2015-05-18 10:30:46

that was tricky (y) :D loved it

Related Questions

Sponsored Content

10 Answered Questions

[SOLVED] How to send FormData objects with Ajax-requests in jQuery?

5 Answered Questions

[SOLVED] appending array to FormData and send via AJAX

2 Answered Questions

[SOLVED] Tool for sending multipart/form-data request

  • 2013-04-15 12:48:47
  • Valentin Despa
  • 279601 View
  • 340 Score
  • 2 Answer
  • Tags:   multipartform-data

1 Answered Questions

[SOLVED] Error 404 (not found) in ajax call with file inside FormData

23 Answered Questions

[SOLVED] jQuery $.ajax(), $.post sending "OPTIONS" as REQUEST_METHOD in Firefox

1 Answered Questions

[SOLVED] php not receives data from ajax FormData

2 Answered Questions

0 Answered Questions

how send file with ajax in codeigniter framework

1 Answered Questions

[SOLVED] multipart/formdata is not sending file data with jQuery.ajax

1 Answered Questions

[SOLVED] $.ajax Sending File formdata along with variables

Sponsored Content