用JQuel.Ajax发送multipart/formdata

Sending multipart/formdata with jQuery.ajax

使用jquery的ajax函数向服务器端的php脚本发送文件时遇到问题。可以使用$('#fileinput').attr('files')获取文件列表,但如何将这些数据发送到服务器?使用文件输入时,服务器端php脚本上的结果数组($_POST为0(NULL)。

我知道这是可能的(尽管我到目前为止还没有找到任何jquery解决方案,但只有原型代码(http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress.html))。

这似乎是相对较新的,所以请不要提及通过XHR/AJAX上传文件是不可能的,因为它确实有效。

我需要Safari 5的功能,FF和Chrome很好,但不是必需的。

我现在的代码是:

1
2
3
4
5
6
7
8
9
10
11
$.ajax({
    url: 'php/upload.php',
    data: $('#file').attr('files'),
    cache: false,
    contentType: 'multipart/form-data',
    processData: false,
    type: 'POST',
    success: function(data){
        alert(data);
    }
});


从Safari 5/firefox 4开始,最容易使用FormData类:

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

所以现在您有了一个FormData对象,可以和xmlhttprequest一起发送了。

1
2
3
4
5
6
7
8
9
10
11
12
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);
    }
});

必须将contentType选项设置为false,强制jquery不为您添加Content-Type头,否则将丢失边界字符串。此外,您必须将processData标志设置为false,否则jquery将尝试将您的FormData转换为字符串,但这将失败。

现在可以使用以下方法在PHP中检索文件:

1
$_FILES['file-0']

(只有一个文件,file-0,除非您在文件输入上指定了multiple属性,在这种情况下,数字将随每个文件递增。)

对旧浏览器使用FormData仿真

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);

从现有表单创建FormData

也可以使用现有表单对象的内容创建FormData对象,而不是手动迭代文件:

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

使用PHP本机数组而不是计数器

只需将文件元素命名为相同,并在括号内结束名称:

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

EDOCX1×9将是一个包含文件上传字段的数组,用于上传的每个文件。事实上,我在最初的解决方案中推荐了这一点,因为迭代过程比较简单。


只是想给拉斐尔的一个伟大的答案加一点。下面是如何让PHP产生相同的EDCOX1×10,而不管您是否使用JavaScript提交。

HTML表单:

1
2
3
4
5
<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>

当不使用javascript提交时,php生成此$_FILES

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
                )

        )

)

如果您进行渐进式增强,使用Raphael的JS提交文件…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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);
    }
});

…这就是PHP的$_FILES数组在使用javascript提交之后的样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
        )

)

这是一个很好的数组,实际上有些人把$_FILES转换成了什么,但是我发现使用相同的$_FILES是很有用的,不管是否使用了javascript提交。所以,下面是对JS的一些小改动:

1
2
3
4
5
6
7
8
9
10
// 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);
});

(2017年4月14日编辑:我从formdata()的构造函数中删除了表单元素——这在Safari中修复了此代码。)

这个代码有两个作用。

  • 自动检索inputname属性,使HTML更易于维护。现在,只要form有了类putimages,其他的一切都会自动处理。也就是说,input不需要有任何特殊的名称。
  • 普通HTML提交的数组格式由data.append行中的javascript重新创建。注意括号。
  • 通过这些更改,用javascript提交现在可以生成与用简单HTML提交完全相同的$_FILES数组。


    我只是根据我读到的一些信息构建了这个函数。

    使用它就像使用.serialize()一样,只需放入.serializefiles();即可。
    在我的测试中工作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //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);


    看看我的代码,它为我完成了任务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $( '#formId' )
      .submit( function( e ) {
        $.ajax( {
          url: 'FormSubmitUrl',
          type: 'POST',
          data: new FormData( this ),
          processData: false,
          contentType: false
        } );
        e.preventDefault();
      } );

    如果表单是在HTML中定义的,那么将表单传递到构造函数比迭代和添加图像更容易。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    $('#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){
                ...

    Devin Venable的回答接近我想要的,但我想要一个可以处理多个表单的答案,并使用表单中已经指定的操作,以便每个文件都可以放到正确的位置。

    我还想使用jquery的on()方法,这样可以避免使用.ready()。

    这让我明白了:(用jQuery选择器替换FultS选择器)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $(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.
        });

    });


    formdata类确实有效,但是在iOS Safari(至少在iPhone上)中,我不能像现在一样使用Raphael Schweikert的解决方案。

    Mozilla Dev有一个关于操作FormData对象的好页面。

    因此,在页面的某个位置添加一个空表单,指定enctype:

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

    然后,将FormData对象创建为:

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

    按照拉斐尔的法典进行。


    nova days您甚至不需要jquery:)获取api支持表

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


    上面所有的解决方案看起来都很好和优雅,但是formData()对象不需要任何参数,而是在实例化之后使用append(),就像上面写的那样:

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


    旧版本的IE不支持FormData(完整的FormData浏览器支持列表如下:https://developer.mozilla.org/en-us/docs/web/api/formdata)。

    您可以使用jquery插件(例如,http://malsup.com/jquery/form/代码示例),也可以使用基于iframe的解决方案通过ajax发布多部分表单数据:https://developer.mozilla.org/en-us/docs/learn/html/forms/sending_forms_through_javascript


    我今天遇到的一个问题是值得指出的,与这个问题相关的:如果Ajax调用的URL被重定向,那么内容类型"multipart/form data"的头可能会丢失。

    例如,我在发布到http://server.com/context?PARAM= X

    在chrome的"网络"选项卡中,我看到了此请求的正确多部分头,但随后302重定向到http://server.com/context/?param=x(注意上下文后面的斜线)

    重定向期间,多部分头丢失。如果这些解决方案对您不起作用,请确保不会重定向请求。


  • 通过jquery->$获取表单对象("id")[0]
  • 数据=新表单数据($(id")[0]);
  • 好的,数据是你想要的