使用 jQuery.ajax 发送 multipart/formdata

Sending multipart/formdata with jQuery.ajax

提问人:zoku 提问时间:3/22/2011 最后编辑:Adexe Riverazoku 更新时间:12/13/2019 访问量:1065664

问:

我在使用 jQuery 的 ajax-function 将文件发送到服务器端 PHP 脚本时遇到了问题。 可以获取文件列表,但如何将此数据发送到服务器?使用 file-input 时,服务器端 php-script 上生成的数组 () 为 0 ()。$('#fileinput').attr('files')$_POSTNULL

我知道这是可能的(尽管直到现在我还没有找到任何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);
    }
});
jquery ajax 文件上传 multipartform-data 表单数据

评论

1赞 Alejandro García Iglesias 5/14/2013
遗憾的是,使用 FormData 对象不适用于 IE<10。
0赞 yuxhuang 7/12/2013
@GarciaWebDev,您应该将 polyfill 与 Flash 一起使用来支持相同的 API。查看 github.com/Modernizr/Modernizr/wiki/...了解更多信息。
3赞 Shahar 8/20/2015
您可以使用来选择所有输入文件。它只是简单一点。$(':file')
0赞 kitti 4/11/2017
@RameshwarVyevhare 这个答案是在这个问题得到回答五年后发布的。请不要仅仅为了宣传自己的答案而提出类似的问题。
0赞 Xzhibit 1/6/2018
@RameshwarVyevhare 您在链接上的答案也是在原始问题被问到一年后发布的

答:

965赞 Raphael Schweikert 5/12/2011 #1

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

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

所以现在你有一个对象,准备与XMLHttpRequest一起发送。FormData

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

必须将选项设置为 ,强制jQuery不为您添加标头,否则,其中将缺少边界字符串。 此外,您必须将标志设置为 false,否则 jQuery 将尝试将 your 转换为字符串,这将失败。contentTypefalseContent-TypeprocessDataFormData

您现在可以使用以下命令在 PHP 中检索文件:

$_FILES['file-0']

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

对旧版浏览器使用 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']然后将是一个数组,其中包含每个上传的文件的文件上传字段。实际上,我建议使用这个而不是我的初始解决方案,因为它更易于迭代。

评论

0赞 D. Walz 5/17/2023
当发送非文件(例如,作为 json 的对象)时,我的后端在缺少部件的 Content-Type 方面存在问题。因此,请考虑将其包装在 Blob 中,例如data.append('my-object', new Blob([JSON.stringify(myObject)], {type: 'application/json'}))
52赞 ajmicek 1/10/2012 #2

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

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>

PHP 在没有 JavaScript 的情况下提交时会产生这个:$_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的数组的样子,在使用该JavaScript提交之后:$_FILES

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
        )

)

这是一个很好的数组,实际上是一些人转换成的数组,但我发现使用相同的数组很有用,无论是否使用 JavaScript 提交。因此,以下是对 JS 的一些小改动:$_FILES$_FILES

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

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

48赞 evandro777 9/14/2012 #3

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

像使用一样使用它,而不是把 .
在我的测试中工作。
.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赞 Fallenreaper 9/19/2012
我试图让它工作,但它似乎没有将serializefiles()识别为一个函数,尽管这个定义在页面顶部。
1赞 SchurigH 11/20/2013
这对我很好。通过 AJAX 数据参数发送数据并使用 Express 进行分析来获取数据 强大:感谢您提供代码片段:)var data = $("#avatar-form").serializefiles();form.parse(req, function(err, fields, files){
1赞 topkara 2/22/2013 #4

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

并按照拉斐尔的代码进行。

-2赞 user1909226 7/18/2013 #5
  1. 通过 jquery-> $(“#id”)[0] 获取表单对象
  2. 数据 = new FormData($(“#id”)[0]);
  3. 好的,数据是你想要的

评论

0赞 Jamali 5/29/2015
$(“#id”)[0] 首先返回 none 空 <input type=“file” /> 表单,您如何提交整个表单,包括所有 <input type=“file” />?
24赞 Devin Venable 6/3/2014 #6

如果表单是在 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){
            ...
68赞 Asad Ullah 8/30/2014 #7

看看我的代码,它为我完成了工作

$( '#formId' )
  .submit( function( e ) {
    $.ajax( {
      url: 'FormSubmitUrl',
      type: 'POST',
      data: new FormData( this ),
      processData: false,
      contentType: false
    } );
    e.preventDefault();
  } );
1赞 james 3/18/2016 #8

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

例如,我正在向 http://server.com/context?param=x 发帖

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

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

15赞 Karl Henselin 6/16/2016 #9

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

我还想使用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.
    });

});
0赞 sudip 2/8/2018 #10

旧版本的 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

0赞 szatti1489 3/8/2018 #11

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

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

8赞 Alex Nikulin 10/29/2018 #12

现在你甚至不需要jQuery:) fetch API support table

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

评论

0赞 Alexander Zabyshny 5/28/2022
美丽的解决方案)
9赞 OXiGEN 7/2/2019 #13

如果文件输入指示一个数组并标记 ,并且您用 解析整个 ,则无需迭代输入文件。 将自动处理多个文件。namemultipleformFormDataappend()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

生成的条目在 Chrome 开发工具中如下所示:$_FILES

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 报告为空,并且没有 AJAX 抛出任何错误。Chrome 75.0.3770.100 和 PHP 7.0 出现问题。在我的测试集中,似乎只有几十张图像中的一张发生。$_POST$_FILES