YaoRaoLov
2011-03-22 13:52
采纳率: 50%
浏览 465

使用 jQuery.ajax 发送多部分 / 表格数据

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);
    }
});

转载于:https://stackoverflow.com/questions/5392344/sending-multipart-formdata-with-jquery-ajax

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

12条回答 默认 最新

  • perhaps? 2011-05-12 09:36
    已采纳

    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.

    点赞 评论
  • 衫裤跑路 2012-01-10 00:56

    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.

    点赞 评论
  • Didn"t forge 2012-09-14 14:33

    I just built 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);
    
    点赞 评论
  • lrony* 2013-02-22 00:24

    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.

    点赞 评论
  • 妄徒之命 2013-07-18 09:54
    1. get form object by jquery-> $("#id")[0]
    2. data = new FormData($("#id")[0]);
    3. ok,data is your want
    点赞 评论
  • perhaps? 2014-06-02 17:43

    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){
                ...
    
    点赞 评论
  • perhaps? 2014-08-30 06:49

    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();
      } );
    
    点赞 评论
  • 三生石@ 2016-03-17 22:07

    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.

    点赞 评论
  • 三生石@ 2016-06-15 16:54

    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.
        });
    
    });
    
    点赞 评论
  • perhaps? 2018-02-08 10:42

    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

    点赞 评论
  • YaoRaoLov 2018-03-08 13:47

    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);

    点赞 评论
  • derek5. 2018-10-29 11:26

    Nova days you not need even jQuery:) fetch API support table

    let result = fetch('url', {method: 'POST', body: new FormData(documemt.querySelector("#form"))})
    
    点赞 评论

相关推荐 更多相似问题