使用jQuery.ajax发送multipart / formdata


563

我在使用jQuery的ajax函数将文件发送到服务器端PHP脚本时遇到问题。可以获取文件列表,$('#fileinput').attr('files')但如何将这些数据发送到服务器呢?使用文件输入时$_POST,服务器端php脚本上的结果数组()为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);
    }
});

遗憾的是,在IE <10上无法使用FormData对象。
亚历杭德罗·加西亚·伊格莱西亚斯

@GarciaWebDev应该可以与Flash一起使用polyfill来支持相同的API。查阅github.com/Modernizr/Modernizr/wiki/…了解更多信息。
yuxhuang


3
您可以$(':file')用来选择所有输入文件。这只是简单一点。
Shahar

@RameshwarVyevhare这个问题被回答五年后发布。请不要仅仅为了推广自己的答案而抛出类似的问题。
Ryan P

Answers:


882

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

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

因此,现在您有了一个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);
    }
});

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

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

$_FILES['file-0']

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

从现有表单创建FormData

除了手动迭代文件,还可以使用现有表单对象的内容来创建FormData对象:

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

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

只需将文件元素命名为相同,并在方括号中加上名称即可:

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

$_FILES['file']然后将是一个数组,其中包含每个上载文件的文件上载字段。实际上,我建议在我最初的解决方案中使用它,因为它更容易迭代。


2
另外,还有一个FormData仿真,它将使该解决方案向旧版浏览器的移植非常简单。您要做的就是手动检查data.fake和设置contentType属性,并覆盖要使用的xhr sendAsBinary()
拉斐尔·施维克

13
因此,您既不使用jQuery也不使用FormData类,而是在针对jQuery的特定问题和针对使用FormData的特定问题的背景下问我?很抱歉,但我认为我不能在这里为您提供帮助
Raphael Schweikert 2012年

3
这使用application/x-www-form-urlencoded。有办法multipart/form-data代替吗?
Timmmm 2013年

4
@Timmmm不,它使用multipart/form-data。使用application/x-www-form-urlencoded将不起作用。
Raphael Schweikert

5
@RoyiNamir 恐怕只有代码记录
拉斐尔·史威克

51

只是想在Raphael的出色答案中添加一点点。$_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>

$_FILES当不使用JavaScript提交时,PHP会生成this :

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提交文件...

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

...这是$_FILES使用该JavaScript提交后,PHP 数组的外观:

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的一些细微更改:

// 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中修复了此代码。)

该代码有两件事。

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

经过这些更改,使用JavaScript提交现在可以产生$_FILES与使用简单HTML提交完全相同的数组。


Safari也有同样的问题。感谢您的提示!
medoingthings'Aug

49

看我的代码,它对我有用

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

这非常有效,并且实现起来非常简单。
Jh62

45

我只是根据阅读的一些信息构建了此功能。

像使用一样使用它.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);

2
我试图使它正常工作,但是尽管此定义位于页面顶部,但它似乎无法将serializefiles()识别为函数。
Fallenreaper 2012年

1
对我来说很好 获取数据与 var data = $("#avatar-form").serializefiles();通过AJAX数据参数发送这一点,并明确强大的分析:form.parse(req, function(err, fields, files){感谢你的代码片段:)
SchurigH

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){
            ...

9

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

});


1

FormData类确实有效,但是在iOS Safari(至少在iPhone上)中,我无法按原样使用Raphael Schweikert的解决方案。

Mozilla Dev 在处理FormData对象方面有一个不错的页面

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

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

然后,将FormData对象创建为:

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

并按照Raphael的代码进行


我的jquery ajax上传文件无提示地挂在Safari中时出现问题,最终为Safari做了一个有条件的浏览器$('form-name')。submit(),而不是在IE9和FF18中可用的ajax上传文件。可能不是多上传的理想解决方案,但是我正在将单个文件从jquery对话框导入到iframe中,因此它可以正常工作。
glyph 2013年

1

我今天遇到的一个陷阱值得一提,与这个问题有关:如果ajax调用的url被重定向,则content-type:'multipart / form-data'的标头可能会丢失。

例如,我发布到 http://server.com/context?param=x

在Chrome的网络标签中,我看到了针对此请求的正确的多部分标题,但随后出现了302重定向到http://server.com/context/?param=x(请注意上下文后的斜杠)

在重定向过程中,多部分标头丢失了。如果这些解决方案不适合您,请确保不会重定向请求。


1

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

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


1

如果文件输入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);
    }
  });
});
<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>

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

结果$_FILES条目在Chrome开发者工具中如下所示:

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"]

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



0

的PHP

<?php 
    var_dump( $_FILES ); 
?>

jQuery和HTML表单

$( "#form" ).bind( "submit", function (e) {

    e.preventDefault();

    $.ajax({

        method: "post",
        url: "process.php",

        data: new FormData( $(this) ), // $(this) = formHTML element

        cache: false,

        /* upload */
        contentType: false,
        processData: false

    });

} )
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

<form id="form" >
    File: <input type="file" name="upload" />
    <input type="submit" value="Submit" />
</form>


-1
  1. 通过jquery-> $(“#id”)[0]获取表单对象
  2. 数据=新的FormData($(“#id”)[0]);
  3. 好的,数据就是你想要的

$(“#id”)[0]返回第一个无空的<input type =“ file” />表单,如何提交整个表单,包括表单的所有<input type =“ file” />?
穆罕默德·侯赛因·贾马利
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.