使用 jQuery.ajax 发送 multipart/formdata
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5392344/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Sending multipart/formdata with jQuery.ajax
提问by zoku
I've got a problem sending a file to a serverside PHP-script using jQuery's ajax-function.
It's possible to get the File-List with $('#fileinput').attr('files')
but how is it possible to send this Data to the server? The resulting array ($_POST
) on the serverside php-script is 0 (NULL
) when using the file-input.
我在使用 jQuery 的 ajax 函数将文件发送到服务器端 PHP 脚本时遇到问题。可以获取文件列表,$('#fileinput').attr('files')
但是如何将此数据发送到服务器?使用文件输入时$_POST
,服务器端 php-script 上的结果数组 ( ) 为 0 ( NULL
)。
I know it is possible (though I didn't find any jQuery solutions until now, only Prototye code (http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress.html)).
我知道这是可能的(尽管直到现在我还没有找到任何 jQuery 解决方案,只有 Prototye 代码(http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress.html) )。
This seems to be relatively new, so please do not mention file upload would be impossible via XHR/Ajax, because it's definitely working.
这似乎相对较新,所以请不要提及无法通过 XHR/Ajax 上传文件,因为它绝对有效。
I need the functionality in Safari 5, FF and Chrome would be nice but are not essential.
我需要 Safari 5、FF 和 Chrome 中的功能会很好但不是必需的。
My code for now is:
我现在的代码是:
$.ajax({
url: 'php/upload.php',
data: $('#file').attr('files'),
cache: false,
contentType: 'multipart/form-data',
processData: false,
type: 'POST',
success: function(data){
alert(data);
}
});
回答by Raphael Schweikert
Starting with Safari 5/Firefox 4, it's easiest to use the FormData
class:
从 Safari 5/Firefox 4 开始,最容易使用FormData
该类:
var data = new FormData();
jQuery.each(jQuery('#file')[0].files, function(i, file) {
data.append('file-'+i, file);
});
So now you have a FormData
object, ready to be sent along with the XMLHttpRequest.
所以现在您有一个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);
}
});
It's imperative that you set the contentType
option to false
, forcing jQuery not to add a Content-Type
header for you, otherwise, the boundary string will be missing from it.
Also, you must leave the processData
flag set to false, otherwise, jQuery will try to convert your FormData
into a string, which will fail.
必须将contentType
选项设置为false
,强制 jQuery 不Content-Type
为您添加标题,否则,边界字符串将丢失。此外,您必须将该processData
标志设置为 false,否则,jQuery 将尝试将您的FormData
转换为字符串,这将失败。
You may now retrieve the file in PHP using:
您现在可以使用 PHP 检索文件:
$_FILES['file-0']
(There is only one file, file-0
, unless you specified the multiple
attribute on your file input, in which case, the numbers will increment with each file.)
(只有一个文件,file-0
,除非您multiple
在文件输入中指定了属性,在这种情况下,数字将随着每个文件的增加而增加。)
Using the FormData emulationfor older browsers
对旧浏览器使用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);
Create FormData from an existing form
从现有表单创建 FormData
Instead of manually iterating the files, the FormData object can also be created with the contents of an existing form object:
除了手动迭代文件之外,还可以使用现有表单对象的内容创建 FormData 对象:
var data = new FormData(jQuery('form')[0]);
Use a PHP native array instead of a counter
使用 PHP 原生数组而不是计数器
Just name your file elements the same and end the name in brackets:
只需将您的文件元素命名为相同的名称并在括号中结束名称:
jQuery.each(jQuery('#file')[0].files, function(i, file) {
data.append('file[]', file);
});
$_FILES['file']
will then be an array containing the file upload fields for every file uploaded. I actually recommend this over my initial solution as it's simpler to iterate over.
$_FILES['file']
然后将是一个包含每个上传文件的文件上传字段的数组。我实际上在我的初始解决方案中推荐这个,因为它更容易迭代。
回答by ajmicek
Just wanted to add a bit to Raphael's great answer. Here's how to get PHP to produce the same $_FILES
, regardless of whether you use JavaScript to submit.
只是想为拉斐尔的精彩答案添加一点。下面是如何让 PHP 生成相同的$_FILES
,无论您是否使用 JavaScript 提交。
HTML form:
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 produces this $_FILES
, when submitted without JavaScript:
PHP$_FILES
在没有 JavaScript 的情况下提交时会产生这个:
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
)
)
)
If you do progressive enhancement, using Raphael's JS to submit the files...
如果做渐进增强,使用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);
}
});
... this is what PHP's $_FILES
array looks like, after using that JavaScript to submit:
...这是 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
)
)
That's a nice array, and actually what some people transform $_FILES
into, but I find it's useful to work with the same $_FILES
, regardless if JavaScript was used to submit. So, here are some minor changes to the JS:
这是一个很好的数组,实际上有些人会转换$_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);
});
(14 April 2017 edit: I removed the form element from the constructor of FormData() -- that fixed this code in Safari.)
(2017 年 4 月 14 日编辑:我从 FormData() 的构造函数中删除了表单元素 - 在 Safari 中修复了此代码。)
That code does two things.
该代码做了两件事。
- Retrieves the
input
name attribute automatically, making the HTML more maintainable. Now, as long asform
has the class putImages, everything else is taken care of automatically. That is, theinput
need not have any special name. - The array format that normal HTML submits is recreated by the JavaScript in the data.append line. Note the brackets.
input
自动检索name 属性,使 HTML 更易于维护。现在,只要form
有 putImages 类,其他一切都会自动处理。也就是说,input
不需要有任何特殊的名称。- 普通 HTML 提交的数组格式由 JavaScript 在 data.append 行中重新创建。注意括号。
With these changes, submitting with JavaScript now produces precisely the same $_FILES
array as submitting with simple HTML.
通过这些更改,使用 JavaScript 提交现在生成$_FILES
与使用简单 HTML 提交完全相同的数组。
回答by Asad Malik
Look at my code, it does the job for me
看看我的代码,它对我有用
$( '#formId' )
.submit( function( e ) {
$.ajax( {
url: 'FormSubmitUrl',
type: 'POST',
data: new FormData( this ),
processData: false,
contentType: false
} );
e.preventDefault();
} );
回答by evandro777
I just built this function based on some info I read.
我只是根据我阅读的一些信息构建了这个函数。
Use it like using .serialize()
, instead just put .serializefiles();
.
Working here in my tests.
像 using 一样使用它.serialize()
,而只是 put .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);
回答by Devin Venable
If your form is defined in your HTML, it is easier to pass the form into the constructor than it is to iterate and add images.
如果您的表单是在 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){
...
回答by Karl Henselin
Devin Venable's answerwas close to what I wanted, but I wanted one that would work on multiple forms, and use the action already specified in the form so that each file would go to the right place.
Devin Venable 的回答与我想要的很接近,但我想要一个可以处理多种表单的答案,并使用表单中已经指定的操作,以便每个文件都可以转到正确的位置。
I also wanted to use jQuery's on() method so I could avoid using .ready().
我还想使用 jQuery 的 on() 方法,这样我就可以避免使用 .ready()。
That got me to this: (replace formSelector with your jQuery selector)
这让我明白了:(用你的 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.
});
});
回答by Alex Nikulin
Nowadays you don't even need jQuery:) fetch API support table
现在你甚至不需要 jQuery:)获取 API 支持表
let result = fetch('url', {method: 'POST', body: new FormData(document.querySelector("#form"))})
回答by OXiGEN
If the file input name
indicates an array and flags multiple
, and you parse the entire form
with FormData
, it is not necessary to iteratively append()
the input files. FormData
will automatically handle multiple files.
如果文件输入name
指示阵列和标志multiple
,你解析整个form
用FormData
,没有必要反复地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>
Note that a regular button type="button"
is used, not type="submit"
. This shows there is no dependency on using submit
to get this functionality.
请注意,使用的是常规button type="button"
,而不是type="submit"
. 这表明不依赖于使用submit
来获得此功能。
The resulting $_FILES
entry is like this in Chrome dev tools:
$_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"]
Note: There are cases where some images will upload just fine when uploaded as a single file, but they will fail when uploaded in a set of multiple files. The symptom is that PHP reports empty $_POST
and $_FILES
without AJAX throwing any errors. Issue occurs with Chrome 75.0.3770.100 and PHP 7.0. Only seems to happen with 1 out of several dozen images in my test set.
注意:在某些情况下,某些图像在作为单个文件上传时可以正常上传,但在一组多个文件中上传时会失败。症状是 PHP 报告为空$_POST
并且$_FILES
没有 AJAX 抛出任何错误。Chrome 75.0.3770.100 和 PHP 7.0 出现问题。在我的测试集中,似乎只有几十张图像中的 1 张发生。
回答by szatti1489
All the solutions above are looks good and elegant, but the FormData() object does not expect any parameter, but use append() after instantiate it, like what one wrote above:
上面的所有解决方案看起来都很好很优雅,但是 FormData() 对象不需要任何参数,而是在实例化之后使用 append() ,就像上面写的那样:
formData.append(val.name, val.value);
formData.append(val.name, val.value);
回答by james
One gotcha I ran into today I think is worth pointing out related to this problem: if the url for the ajax call is redirected then the header for content-type: 'multipart/form-data' can be lost.
我今天遇到的一个问题我认为值得指出与这个问题相关的问题:如果 ajax 调用的 url 被重定向,那么 content-type: 'multipart/form-data' 的标头可能会丢失。
For example, I was posting to http://server.com/context?param=x
例如,我发布到http://server.com/context?param=x
In the network tab of Chrome I saw the correct multipart header for this request but then a 302 redirect to http://server.com/context/?param=x(note the slash after context)
在 Chrome 的网络选项卡中,我看到了此请求的正确多部分标头,但随后 302 重定向到http://server.com/context/?param=x(注意上下文后的斜杠)
During the redirect the multipart header was lost. Ensure requests are not being redirected if these solutions are not working for you.
在重定向期间,多部分标头丢失。如果这些解决方案不适合您,请确保不会重定向请求。