Stopbyte

How to send multipart/formdata with jQuery and Ajax?

Hi,

How can I send multipart FormData using JQuery.ajax? I know it’s possible to do it, but the code I have below, is returning NULL on my server side. I have no clue why that is happening at all.
I am trying to upload a list of files to the server side using the code below:

$.ajax({
    type: 'POST',
    cache: false,
    contentType: 'multipart/form-data',
    processData: false,
    url: 'localhost:2458/backend/upload.php',
    data: $('#fileInputBox').attr('files'),

    success: function(data) {
        alert("Data sending was successful");
    }
});

Any help would be appreciated.
Thanks

3 Likes

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,
    type: 'POST',
    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,
    type: 'POST',
    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.

1 Like

I believe you better just do it using HTML? that’s the easiest way I know to upload a series of files to your backend server, but Javascript is possible as well. Here is how you can do it:

1. Using Pure HTML Form

1.1 Frontend

When using a pure HTML form, you’ll need the enctype attribute that specifies the encoding used to send data to your server. Set it to "multipart/form-data" for this to work.

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

P.S.
enctype attribute should only be used with method="post".

1.2 Backend

The HTML form above, will produce the following data structure at the PhP server backend (this is just sample data):

Array {
    media:Array {
            name:Array {
                    /* List of file names, under the name array. */
                    [0] => MyTextFile.txt
                    [1] => HelloWorld.png
                    [2] => HelloWorld.csv
            }
            type:Array {
                    [0] => text/plain
                    [1] => image/png
                    [2] => text/plain
            }
            tmp_name:Array {
                    [0] => /tmp/someBlahBlahBlah1
                    [1] => /tmp/someBlahBlahBlah2
                    [1] => /tmp/someBlahBlahBlah3
            }
            error:Array {
                    [0] => 0
                    [1] => 0
                    [2] => 0
            }
            size:Array {
                    [0] => 785459
                    [1] => 784512
                    [1] => 885421
            }
    } /* end of media array */
} /* end of root array */

##2. Using JQuery.Ajax

2.1. Frontend JQuery.Ajax

Apparently the mistake in your code sneppit is that you’ve been sending out the files in the same format you get them through $('#fileInputBox').attr('files'), which is completely wrong:

Instead you should be doing, is first generating a FormData based on the files the user picked to upload, and then sending out that FormData to the server backend, something as follows should do the trick:

var form_data = new FormData($('input[name^="media"]'));

/* First build a FormData of files to be sent out to the server-side */
jQuery.each($('input[name^="media"]')[0].files, function(i, file) {
    form_data.append(i, file);
});

/* Now send the gathered files data to our backend server */
iQuery.ajax({
    type: 'POST',
    cache: false,
    processData: false,
    contentType: false,
    data: form_data,
    url: '/backend/upload.php',

    success: function(data) {
        /* The data has been sent out successfully. */
    }
});

2.2. PhP Backend

For the Ajax code snippet above, the PhP backend server $_FILES global variable, would be structured and a totally different way, so make sure to notice that. Here is a sample data structure you’ll be receiving on the server-end:

Array {
    [0] => Array {
            name => MyTxtFile.txt
            type => plain/text
            tmp_name => /tmp/generated_name_here
            error => 0 /* error code, if the upload failed. */
            size => 777474
    }
    .....
    [999] => Array {
            name => MyJpgImage.jpg
            type => image/jpeg
            tmp_name => /tmp/generated_name_here2 /* this is the server location of the file on your backend if the upload has been successful */
            error => 0
            size => 85474
    }
}

When comparing the two structures together (the one we recieved using the HTML form, and the Ajax call), you’ll notice that the ajax call using a FormData tends to cluster every file’s attributes into a single array, rather than having them all split around within different arrays. Which can be counted as a plus in many cases and makes the files array easier to browse.

That’s it, Hope that helps you out.

2 Likes

Your ajax file should look something like this:

$(document).ready(function (e) {
 $("#form").on('submit',(function(e) {
  e.preventDefault();
  $.ajax({
     url: "ajaxupload.php",
   type: "POST",
   data:  new FormData(this),
   contentType: false,
     cache: false,
   processData:false,
   beforeSend : function()
   {
//$("#preview").fadeOut();
$("#err").fadeOut();
   },
   success: function(data)
  {
if(data=='invalid')
{
 // invalid file format.
 $("#err").html("Invalid File !").fadeIn();
}
else
{
 // view uploaded file.
 $("#preview").html(data).fadeIn();
 $("#form")[0].reset(); 
}
  },
 error: function(e) 
  {
$("#err").html(e).fadeIn();
  }          
});
 }));
});

Source: PHP File Upload with Ajax

1 Like