用jQuery.ajax发送multipart / formdata

我有一个问题,使用jQuery的ajax函数发送文件到服务器端的PHP脚本。 可以用$('#fileinput').attr('files')获得文件列表,但是如何将这个数据发送到服务器呢? 使用文件input时,服务器端php脚本的结果数组( $_POST )为0( NULL )。

我知道这是可能的(尽pipe直到现在我还没有find任何jQuery解决scheme,只有Prototye代码( http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress.html ) )。

这似乎是比较新的,所以请不要提到file upload将不可能通过XHR / Ajax,因为它肯定是工作。

我需要在Safari 5中的function,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); } }); 

从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头,否则,边界string将会被丢失。 此外,您必须将processData标志设置为false,否则,jQuery将尝试将您的FormData转换为string,这将失败。

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

 $_FILES['file-0'] 

(只有一个文件, file-0 ,除非在文件input中指定了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); 

从现有的表单创buildFormData

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

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

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

只要命名您的文件元素相同,并在括号中结束名称:

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

$_FILES['file']将成为一个数组,其中包含上传的每个文件的file upload字段。 实际上,我推荐使用最初的解决scheme,因为迭代更简单。

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

像使用.serialize()一样使用它,而不是把.serializefiles();
在我的testing中在这里工作。

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

只是想添加一点拉斐尔的伟大答案。 无论您是否使用JavaScript进行提交,以下是如何让PHP生成相同的$_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 ) ) ) 

如果你做渐进增强,使用拉斐尔的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是有用的,不pipe是否使用了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重新创build的。 请注意括号。

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

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

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

如果您的表单是在您的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){ ... 

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

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

这让我到这个:(用你的jQueryselect器replaceformSelector)

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

FormData类可以工作,但是在iOS Safari中(至less在iPhone上),我无法使用Raphael Schweikert的解决scheme。

Mozilla Dev 在操作FormData对象上有一个不错的页面。

所以,在页面的某个地方添加一个空的表单,指定enctype:

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

然后,创buildFormData对象为:

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

并按照Raphael的代码进行 。

我今天碰到一个问题,我认为值得指出与这个问题有关:如果ajax调用的url被redirect,那么content-type:'multipart / form-data'的头部可能会丢失。

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

在Chrome的networking选项卡中,我看到了这个请求的正确的多部分头,然后302redirect到http://server.com/context/?param=x (注意上下文之后的斜杠)

在redirect期间,多部分头部丢失。 如果这些解决scheme不适合您,请确保请求不被redirect。

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

作为AJAX的替代scheme,您可以在文档中添加隐藏的iframe,将表单复制并发布(以便在可见页面中不发生redirect)。 我想你可以在之后删除iframe。

(HTML和JS是黑客技术,而不是程序员,你不得不破解你的出路……上帝知道补丁和“演化”JS和HTML多less年了,你仍然可以“如果不使用外部库文件,就做这么简单的工作,我感到厌倦了(我知道HTML5是不够的,也没有得到广泛的支持))