使用jQuery.ajax发送multipart / formdata

时间:2011-03-22 13:52:37

标签: jquery ajax file-upload multipartform-data form-data

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

14 个答案:

答案 0 :(得分:824)

从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 emulation

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']将成为包含上传的每个文件的文件上载字段的数组。实际上我推荐这个在我的初始解决方案中,因为它更容易迭代。

答案 1 :(得分:47)

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

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>

在没有JavaScript的情况下提交时,PHP会生成此$_FILES

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

...这是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
        )

)

这是一个很好的数组,实际上有些人将$_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名称属性,使HTML更易于维护。现在,只要form有类putImages,其他所有内容都会自动处理。也就是说,input不需要任何特殊名称。
  2. 普通HTML提交的数组格式由data.append行中的JavaScript重新创建。注意括号。
  3. 通过这些更改,现在使用JavaScript提交与使用简单HTML提交的$_FILES数组完全相同。

答案 2 :(得分: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);

答案 3 :(得分:43)

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

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

答案 4 :(得分: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){
            ...

答案 5 :(得分:7)

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

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

这让我这样: (将formSelector替换为jQuery选择器)

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

});

答案 6 :(得分:1)

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

Mozilla Dev有一个很好的页面on manipulating FormData objects

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

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

然后,将FormData对象创建为:

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

并按照Raphael's code进行操作。

答案 7 :(得分:1)

上面的所有解决方案看起来都很优雅,但FormData()对象并不期望任何参数,但在实例化后使用append(),就像上面写的那样:

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

答案 8 :(得分:0)

今天遇到的一个问题我认为值得指出与此问题相关:如果ajax调用的url被重定向,那么内容类型的标题:&#39; multipart / form-data&#39;可能会丢失。

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

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

在重定向期间,multipart标头丢失了。如果这些解决方案不适合您,请确保不会重定向请求。

答案 9 :(得分:0)

较旧版本的IE不支持FormData(FormData的完整浏览器支持列表位于:https://developer.mozilla.org/en-US/docs/Web/API/FormData)。

您可以使用jquery插件(例如,http://malsup.com/jquery/form/#code-samples)或者,您可以使用基于IFrame的解决方案通过ajax发布多部分表单数据:https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript

答案 10 :(得分:0)

最近几天甚至都不需要jQuery :) fetch API support table

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

答案 11 :(得分:0)

如果文件输入name表示一个数组并标记multiple,并且您用form解析了整个FormData,则不必迭代{{1} }输入文件。 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"来获得此功能。

在Chrome开发人员工具中,生成的submit条目如下:

$_FILES

注意:在某些情况下,某些图像作为单个文件上传时会上传得很好,但是在一组多个文件中上传时它们会失败。症状是PHP报告了空的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"] $_POST,而AJAX不会引发任何错误。 Chrome 75.0.3770.100和PHP 7.0出现问题。我的测试集中只有几十个图像中只有1个发生了这种情况。

答案 12 :(得分:0)

PHP

<?php 
    var_dump( $_FILES ); 
?>

jQuery和Form 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>

答案 13 :(得分:-1)

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