【问题标题】:Sending multipart/formdata with jQuery.ajax使用 jQuery.ajax 发送 multipart/formdata
【发布时间】:2011-07-20 12:36:02
【问题描述】:

我在使用 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);
    }
});

【问题讨论】:

  • 遗憾的是使用 FormData 对象在 IE 上不起作用
  • @GarciaWebDev 据说您可以使用带有 Flash 的 polyfill 来支持相同的 API。查看github.com/Modernizr/Modernizr/wiki/… 了解更多信息。
  • 可能duplicate.
  • 您可以使用$(':file') 选择所有输入文件。就是简单一点。
  • @RameshwarVyevhare 该答案是在回答此问题五年后发布的。请不要为了宣传您自己的答案而提出类似的问题。

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


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    只是想为 Raphael 的精彩回答添加一点内容。下面是如何让 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
                    )
    
            )
    
    )
    

    如果你做渐进式增强,使用拉斐尔的 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 重新创建。注意括号。

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

    【讨论】:

      【解决方案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);
      

      【讨论】:

      • 我试图让它工作,但它似乎无法将 serializefiles() 识别为函数,尽管此定义位于页面顶部。
      • 这对我来说很好。使用var data = $("#avatar-form").serializefiles(); 获取数据,通过 ajax 数据参数发送此数据并使用 express 强大的分析:form.parse(req, function(err, fields, files){ 谢谢你的代码 sn-p :)
      【解决方案4】:

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

      Mozilla Dev 有一个不错的页面on manipulating FormData objects

      所以,在页面的某处添加一个空表单,指定编码类型:

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

      然后,创建 FormData 对象为:

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

      然后像Raphael's code一样继续。

      【讨论】:

        【解决方案5】:
        1. 通过jquery获取表单对象-> $("#id")[0]
        2. data = new FormData($("#id")[0]);
        3. 好的,数据是你想要的

        【讨论】:

        • $("#id")[0] 返回表单的第一个空 ,你如何提交整个表单,包括所有 吗?
        【解决方案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){
                    ...
        

        【讨论】:

          【解决方案7】:

          看看我的代码,它对我有用

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

          【讨论】:

            【解决方案8】:

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

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

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

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

            【讨论】:

              【解决方案9】:

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

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

              【讨论】:

                【解决方案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

                【讨论】:

                  【解决方案11】:

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

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

                  【讨论】:

                    【解决方案12】:

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

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

                    【讨论】:

                      【解决方案13】:

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

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

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2012-10-01
                        • 2014-01-08
                        • 1970-01-01
                        相关资源
                        最近更新 更多