使用jQuery.ajax发送multipart/formdata

zok*_*oku 545 ajax jquery file-upload multipartform-data form-data

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

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

这似乎是相对较新的,所以请不要通过XHR/Ajax提及文件上传,因为它肯定有用.

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

我现在的代码是:

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

Rap*_*ert 857

从Safari 5/Firefox 4开始,最简单的方法是使用FormData该类:

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

所以现在你有了一个FormData对象,随时可以与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);
    }
});
Run Code Online (Sandbox Code Playgroud)

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

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

$_FILES['file-0']
Run Code Online (Sandbox Code Playgroud)

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

为旧版浏览器使用FormData仿真

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);
Run Code Online (Sandbox Code Playgroud)

从现有表单创建FormData

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

var data = new FormData(jQuery('form')[0]);
Run Code Online (Sandbox Code Playgroud)

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

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

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

$_FILES['file']然后将是一个包含上传的每个文件的文件上载字段的数组.我实际上推荐这个在我的初始解决方案,因为它更容易迭代.

  • 所以你既不使用`jQuery`也不使用`FormData`类,你问我在特定于jQuery的问题和特定于使用FormData的答案的上下文中?对不起,我不认为我可以帮助你... (12认同)
  • @RoyiNamir我只是记录[在代码中](https://github.com/jquery/jquery/blob/2.0.3/src/ajax.js#L497),我很害怕. (5认同)
  • @Timmmm不,它使用`multipart/form-data`.使用`application/x-www-form-urlencoded`是行不通的. (4认同)
  • 这使用`application/x-www-form-urlencoded`.有没有办法使用`multipart/form-data`代替? (3认同)
  • 此外,还有一个[FormData仿真](https://github.com/francois2metz/html5-formdata),这将使这个解决方案移植到旧版浏览器非常简单.您所要做的就是检查`data.fake`并手动设置`contentType`属性以及覆盖xhr以使用`sendAsBinary()`. (2认同)
  • 你能详细说明"似乎没有用"吗?你究竟尝试了什么?`FormData`仿真还是本机类?使用仿真时,边界位于`data.boundary`中,因此你要添加`xhr:function(){var xhr = jQuery.ajaxSettings.xhr(); xhr.send = xhr.sendAsBinary; 返回xhr; `,`选项并使用``multipart/form-data; boundary ="+ data.boundary`作为`contentType`选项. (2认同)
  • 也许应该省略`cache`选项(参见http://api.jquery.com/jquery.ajax/):"将cache设置为false只能在HEAD和GET请求中正常工作.它的工作原理是附加`_ = { GET参数的时间戳.对于其他类型的请求,该参数不需要,除了在IE8中,当对已经被GET请求的URL进行POST时. (2认同)
  • @RoyiNamir:官方文档已经[修改](http://api.jquery.com/jQuery.ajax/):"从jQuery 1.6开始,你可以传递false来告诉jQuery不要设置任何内容类型标题." (2认同)
  • 根据https://caniuse.com/#search=FormData此解决方案对浏览器的全球支持率为94.7%(按使用情况加权). (2认同)

ajm*_*cek 49

只是想为拉斐尔的回答添加一些内容.$_FILES无论您是否使用JavaScript提交,以下是如何让PHP生成相同的内容.

HTML表单:

<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>
Run Code Online (Sandbox Code Playgroud)

PHP $_FILES在没有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
                )

        )

)
Run Code Online (Sandbox Code Playgroud)

如果你进行渐进增强,使用Raphael的JS提交文件......

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);
    }
});
Run Code Online (Sandbox Code Playgroud)

...这是PHP的$_FILES数组在使用JavaScript提交之后的样子:

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
        )

)
Run Code Online (Sandbox Code Playgroud)

这是一个很好的数组,实际上有些人会变换$_FILES,但我发现使用相同的数据是有用的$_FILES,无论JavaScript是否用于提交.所以,这里有一些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);
});
Run Code Online (Sandbox Code Playgroud)

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

那段代码做了两件事.

  1. input自动检索name属性,使HTML更易于维护.现在,只要form有类putImages,其他一切都会自动处理.也就是说,input不需要任何特殊名称.
  2. 普通HTML提交的数组格式由data.append行中的JavaScript重新创建.注意括号.

通过这些更改,现在使用JavaScript $_FILES提交与使用简单HTML提交的数组完全相同.


Asa*_*lik 47

看看我的代码,它为我做的工作

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


小智 44

我刚刚根据我读过的一些信息构建了这个函数.

使用它就像使用.serialize(),而不是只是把它.serializefiles();.
在我的测试中工作.

//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);
Run Code Online (Sandbox Code Playgroud)

  • 我试图让这个工作,但它似乎不认识serializefiles()作为一个函数,尽管这个定义在页面的顶部. (2认同)

小智 23

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

$('#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){
            ...
Run Code Online (Sandbox Code Playgroud)


Ale*_*lin 8

现在你甚至不需要 jQuery:)获取 API 支持表

let result = fetch('url', {method: 'POST', body: new FormData(document.querySelector("#form"))})
Run Code Online (Sandbox Code Playgroud)


Kar*_*lin 7

Devin Venable的答案与我想要的很接近,但是我想要一个可以在多个表单上工作的答案,并使用表单中已经指定的操作,以便每个文件都可以到达正确的位置.

我也想使用jQuery的on()方法,所以我可以避免使用.ready().

这让我想到了这一点:(用你的jQuery选择器替换formSelector)

$(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.
    });

});
Run Code Online (Sandbox Code Playgroud)


OXi*_*GEN 5

如果文件输入name指示阵列和标志multiple,你解析整个formFormData,没有必要反复地append()输入文件。FormData将自动处理多个文件。

$('#submit_1').on('click', function() {
  let data = new FormData($("#my_form")[0]);

  $.ajax({
    url: '/path/to/php_file',
    type: 'POST',
    data: data,
    processData: false,
    contentType: false,
    success: function(r) {
      console.log('success', r);
    },
    error: function(r) {
      console.log('error', r);
    }
  });
});
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="my_form">
  <input type="file" name="multi_img_file[]" id="multi_img_file" accept=".gif,.jpg,.jpeg,.png,.svg" multiple="multiple" />
  <button type="button" name="submit_1" id="submit_1">Not type='submit'</button>
</form>
Run Code Online (Sandbox Code Playgroud)

请注意,使用的是常规button type="button",而不是type="submit". 这表明不依赖于使用submit来获得此功能。

$_FILESChrome 开发工具中的结果条目是这样的:

multi_img_file:
  error: (2) [0, 0]
  name: (2) ["pic1.jpg", "pic2.jpg"]
  size: (2) [1978036, 2446180]
  tmp_name: (2) ["/tmp/phphnrdPz", "/tmp/phpBrGSZN"]
  type: (2) ["image/jpeg", "image/jpeg"]
Run Code Online (Sandbox Code Playgroud)

注意:在某些情况下,某些图像在作为单个文件上传时可以正常上传,但在一组多个文件中上传时会失败。症状是 PHP 报告为空$_POST并且$_FILES没有 AJAX 抛出任何错误。Chrome 75.0.3770.100 和 PHP 7.0 出现问题。在我的测试集中,似乎只有几十张图像中的 1 张发生。