使用 Ajax 以一种形式上传数据和文件?

Posted

技术标签:

【中文标题】使用 Ajax 以一种形式上传数据和文件?【英文标题】:Uploading both data and files in one form using Ajax? 【发布时间】:2012-06-09 14:14:26 【问题描述】:

我在表单中使用 jQuery 和 Ajax 来提交数据和文件,但我不确定如何在一个表单中同时发送数据和文件?

我目前对这两种方法几乎都做同样的事情,但是将数据收集到数组中的方式不同,数据使用.serialize();,但文件使用= new FormData($(this)[0]);

是否可以结合这两种方法通过 Ajax 以一种形式上传文件和数据?

数据 jQuery、Ajax 和 html

$("form#data").submit(function()

    var formData = $(this).serialize();

    $.ajax(
        url: window.location.pathname,
        type: 'POST',
        data: formData,
        async: false,
        success: function (data) 
            alert(data)
        ,
        cache: false,
        contentType: false,
        processData: false
    );

    return false;
);

<form id="data" method="post">
    <input type="text" name="first" value="Bob" />
    <input type="text" name="middle" value="James" />
    <input type="text" name="last" value="Smith" />
    <button>Submit</button>
</form>

文件 jQuery、Ajax 和 html

$("form#files").submit(function()

    var formData = new FormData($(this)[0]);

    $.ajax(
        url: window.location.pathname,
        type: 'POST',
        data: formData,
        async: false,
        success: function (data) 
            alert(data)
        ,
        cache: false,
        contentType: false,
        processData: false
    );

    return false;
);

<form id="files" method="post" enctype="multipart/form-data">
    <input name="image" type="file" />
    <button>Submit</button>
</form>

如何结合以上内容,以便通过 Ajax 以一种形式发送数据和文件?

我的目标是能够使用 Ajax 在一个帖子中发送所有这些表单,这可能吗?

<form id="datafiles" method="post" enctype="multipart/form-data">
    <input type="text" name="first" value="Bob" />
    <input type="text" name="middle" value="James" />
    <input type="text" name="last" value="Smith" />
    <input name="image" type="file" />
    <button>Submit</button>
</form>

【问题讨论】:

FormData 方法应该适用于包含您想要的任何内容的表单,而不仅仅是文件上传字段;但它并未得到广泛支持。 @lanzz 是哪个?带有序列化的那个似乎只适用于数据,而另一个似乎只适用于文件? 根据this MDN page判断,使用FormData时应提交所有表单数据 @lanzz 你是对的,它的工作原理是我认为应该是我使用了错误的表单 ID,你可以通过一个带有 ajax 的表单上传文件和数据。 当有多选文件输入时,这似乎不起作用。它只上传第一个文件。 【参考方案1】:

我遇到的问题是使用了错误的 jQuery 标识符。

可以通过一种表单使用 ajax 上传数据和文件

PHP + HTML

<?php

print_r($_POST);
print_r($_FILES);
?>

<form id="data" method="post" enctype="multipart/form-data">
    <input type="text" name="first" value="Bob" />
    <input type="text" name="middle" value="James" />
    <input type="text" name="last" value="Smith" />
    <input name="image" type="file" />
    <button>Submit</button>
</form>

jQuery + Ajax

$("form#data").submit(function(e) 
    e.preventDefault();    
    var formData = new FormData(this);

    $.ajax(
        url: window.location.pathname,
        type: 'POST',
        data: formData,
        success: function (data) 
            alert(data)
        ,
        cache: false,
        contentType: false,
        processData: false
    );
);

短版

$("form#data").submit(function(e) 
    e.preventDefault();
    var formData = new FormData(this);    

    $.post($(this).attr("action"), formData, function(data) 
        alert(data);
    );
);

【讨论】:

在 IE $(this)[0] 只是this 的别名,所以new FormData(this) 应该足够了。 似乎无法检查 FormData 对象,请参阅this question(对于任何遇到与我刚才一样的无知的人,因为对象总是空的)。 对于未来的读者:contentType 和 processData 声明很重要。请参阅this answer 了解更多信息。 async: false 似乎不需要此功能并导致移动(单线程)浏览器阻塞【参考方案2】:

另一种选择是使用 iframe 并将表单的目标设置为它。

你可以试试这个(它使用 jQuery):

function ajax_form($form, on_complete)

    var iframe;

    if (!$form.attr('target'))
    
        //create a unique iframe for the form
        iframe = $("<iframe></iframe>").attr('name', 'ajax_form_' + Math.floor(Math.random() * 999999)).hide().appendTo($('body'));
        $form.attr('target', iframe.attr('name'));
    

    if (on_complete)
    
        iframe = iframe || $('iframe[name="' + $form.attr('target') + '"]');
        iframe.load(function ()
        
            //get the server response
            var response = iframe.contents().find('body').text();
            on_complete(response);
        );
    

它适用于所有浏览器,您无需序列化或准备数据。 缺点是您无法监控进度。

另外,至少对于 chrome,请求不会出现在开发者工具的“xhr”选项卡中,而是出现在“doc”下

【讨论】:

确实不是 Ajax,对有同样问题的人仍然有用。 我简直不敢相信为什么这个答案会得到 -2,我最终使用它是因为我需要旧版浏览器支持 这个答案应该在线程中,因为其他答案指出“旧版浏览器不起作用”或“可以使用 iframe hack”,但从未解决它们。一段不错的代码,还展示了如何正确使用 onload +1【参考方案3】:

我在带有 HttpPostedFilebase 的 ASP.Net MVC 中遇到了同样的问题,而不是在提交时使用表单,我需要在单击时使用按钮,我需要在需要做一些事情的地方,然后如果一切正常,提交表单,这就是我的方式搞定了

$(".submitbtn").on("click", function(e) 

    var form = $("#Form");

    // you can't pass Jquery form it has to be javascript form object
    var formData = new FormData(form[0]);

    //if you only need to upload files then 
    //Grab the File upload control and append each file manually to FormData
    //var files = form.find("#fileupload")[0].files;

    //$.each(files, function() 
    //  var file = $(this);
    //  formData.append(file[0].name, file[0]);
    //);

    if ($(form).valid()) 
        $.ajax(
            type: "POST",
            url: $(form).prop("action"),
            //dataType: 'json', //not sure but works for me without this
            data: formData,
            contentType: false, //this is requireded please see answers above
            processData: false, //this is requireded please see answers above
            //cache: false, //not sure but works for me without this
            error   : ErrorHandler,
            success : successHandler
        );
    
);

这将正确地填充您的 MVC 模型,请确保在您的模型中,HttpPostedFileBase[] 的属性与 html 中输入控件的 名称 具有相同的名称,即

<input id="fileupload" type="file" name="UploadedFiles" multiple>

public class MyViewModel

    public HttpPostedFileBase[] UploadedFiles  get; set; 

【讨论】:

你是一个节省时间的人。:) 就我而言,我不得不使用:contentType : "application/octet-stream" 谢谢哥们!你节省了很多时间。 谢谢伙计!以下两行对我有用。 var form = $("#Form"); var formData = new FormData(form[0]);【参考方案4】:

或更短:

$("form#data").submit(function() 
    var formData = new FormData(this);
    $.post($(this).attr("action"), formData, function() 
        // success    
    );
    return false;
);

【讨论】:

那么,如何使用相同的脚本验证数据字段,即如果您的表单中有一个文本字段和一个文件字段【参考方案5】:

对我来说,如果没有 Ajax 请求中的 enctype: 'multipart/form-data' 字段,它就无法工作。我希望它可以帮助陷入类似问题的人。

尽管enctype 已经在表单属性中设置,但由于某种原因,Ajax 请求在没有明确声明的情况下不会自动识别enctype (jQuery 3.3.1)。

// Tested, this works for me (jQuery 3.3.1)

fileUploadForm.submit(function (e)    
    e.preventDefault();
    $.ajax(
            type: 'POST',
            url: $(this).attr('action'),
            enctype: 'multipart/form-data',
            data: new FormData(this),
            processData: false,
            contentType: false,
            success: function (data) 
                console.log('Thank God it worked!');
            
        
    );
);

// enctype field was set in the form but Ajax request didn't set it by default.

<form action="process/file-upload" enctype="multipart/form-data" method="post" >

     <input type="file" name="input-file" accept="text/plain" required> 
     ...
</form>

正如上面提到的其他人,还请特别注意contentTypeprocessData 字段。

【讨论】:

"对我来说,没有 enctype: 'multipart/form-data' 字段在 Ajax 请求中是行不通的。" ——那不可能有任何影响。它不是 jQuery.ajax 识别的属性。请参阅the documentation,其中根本没有提到enctype 就像我之前提到的,我尝试了多个不同的答案,但都没有奏效。 JS 控制台中显示 Ajax 错误,说明编码错误。后来我关注了this tutorial,它终于让我的代码工作了,我把它贴在这里。也许,enctype 字段未包含在文档中是有原因的。我没有检查 jQuery 源代码,所以我不能肯定地说。 “也许,enctype 字段未包含在文档中是有原因的。” ——那是因为 jQuery 没有用它做任何事情,所以这是无稽之谈。 也许你可以contact Mkyong 和他对话。我通过删除enctype 字段再次测试了我的代码,它不再上传文件(编码类型错误返回)。我不确定它是如何工作的,因为我没有检查 jQuery 源代码。我发布此答案的目的是帮助陷入类似问题的其他人。我不是在这里钓鱼...如果您还有其他问题/cmets,让我们聊天而不是评论。【参考方案6】:

一种简单但更有效的方法:new FormData() 本身就像一个容器(或袋子)。您可以将所有 attr 或 file 放入其中。 您唯一需要附加attribute, file, fileName 例如:

let formData = new FormData()
formData.append('input', input.files[0], input.files[0].name)

然后在 AJAX 请求中传递它。例如:

    let formData = new FormData()
    var d = $('#fileid')[0].files[0]

    formData.append('fileid', d);
    formData.append('inputname', value);

    $.ajax(
        url: '/yourroute',
        method: 'POST',
        contentType: false,
        processData: false,
        data: formData,
        success: function(res)
            console.log('successfully')
        ,
        error: function()
            console.log('error')
        
    )

您可以使用 FormData 附加 n 个文件或数据。

如果您从 Script.js 文件向 Node.js 中的 Route 文件发出 AJAX 请求,请注意使用 req.body 访问数据(即文本)req.files 访问文件(即图片、视频等)

【讨论】:

【参考方案7】:

下面的代码对我有用

$(function () 
    debugger;
    document.getElementById("FormId").addEventListener("submit", function (e) 
        debugger;
        if (ValidDateFrom())  // Check Validation 
            var form = e.target;
            if (form.getAttribute("enctype") === "multipart/form-data") 
                debugger;
                if (form.dataset.ajax) 
                    e.preventDefault();
                    e.stopImmediatePropagation();
                    var xhr = new XMLHttpRequest();
                    xhr.open(form.method, form.action);
                    xhr.onreadystatechange = function (result) 
                        debugger;
                        if (xhr.readyState == 4 && xhr.status == 200) 
                            debugger;
                            var responseData = JSON.parse(xhr.responseText);
                            SuccessMethod(responseData); // Redirect to your Success method 
                        
                    ;
                    xhr.send(new FormData(form));
                
            
        
    , true);
);

在您的 Action Post 方法中,将参数作为 HttpPostedFileBase UploadFile 传递,并确保您的文件输入与您在 Action 方法的参数中提到的相同。 它也应该适用于 AJAX Begin 表单。

请记住,您的 AJAX BEGIN 表单在此处无法正常工作,因为您在上述代码中定义了 post call,并且您可以根据要求在代码中引用您的方法

我知道我回复晚了,但这对我有用

【讨论】:

【参考方案8】:

这是我实施的解决方案

var formData = new FormData();
var files = $('input[type=file]');
for (var i = 0; i < files.length; i++) 
if (files[i].value == "" || files[i].value == null) 
    return false;

else 
    formData.append(files[i].name, files[i].files[0]);


var formSerializeArray = $("#Form").serializeArray();
for (var i = 0; i < formSerializeArray.length; i++) 
  formData.append(formSerializeArray[i].name, formSerializeArray[i].value)

$.ajax(
 type: 'POST',
 data: formData,
 contentType: false,
 processData: false,
 cache: false,
 url: '/Controller/Action',
 success: function (response) 
    if (response.Success == true) 
        return true;
    
    else 
        return false;
    
 ,
 error: function () 
    return false;
 ,
 failure: function () 
    return false;
 
 );

【讨论】:

【参考方案9】:

在我的情况下,我必须发出一个 POST 请求,其中包含通过标头发送的信息,以及使用 FormData 对象发送的文件。

我在这里使用了一些答案的组合使它起作用,所以基本上最终起作用的是在我的 Ajax 请求中包含这五行:

 contentType: "application/octet-stream",
 enctype: 'multipart/form-data',
 contentType: false,
 processData: false,
 data: formData,

formData 是这样创建的变量:

 var file = document.getElementById('uploadedFile').files[0];
 var form = $('form')[0];
 var formData = new FormData(form);
 formData.append("File", file);

【讨论】:

contentType: "application/octet-stream", 是非常有害的,它不会引起问题的唯一原因是因为你在两行之后覆盖了它。 enctype: 'multipart/form-data', 毫无意义。 jQuery.ajax 无法识别该参数。 ...您的其余答案未能涵盖问题标题中“数据文件”的“数据”位。【参考方案10】:

您可以将它们附加到您的表单数据中,在其中添加您的文件和数据。您可以阅读此内容..

https://developer.mozilla.org/en-US/docs/Web/API/FormData/append

为了更好地理解。您可以分别为您的文件检索它们 $_FILES 和为您的数据检索 $_POST。

【讨论】:

【参考方案11】:
<form id="form" method="post" action="otherpage.php" enctype="multipart/form-data">
    <input type="text" name="first" value="Bob" />
    <input type="text" name="middle" value="James" />
    <input type="text" name="last" value="Smith" />
    <input name="image" type="file" />
    <button type='button' id='submit_btn'>Submit</button>
</form>

<script>
$(document).on("click", "#submit_btn", function (e) 
    //Prevent Instant Click  
    e.preventDefault();
    // Create an FormData object 
    var formData = $("#form").submit(function (e) 
        return;
    );
    //formData[0] contain form data only 
    // You can directly make object via using form id but it require all ajax operation inside $("form").submit(<!-- Ajax Here   -->)
    var formData = new FormData(formData[0]);
    $.ajax(
        url: $('#form').attr('action'),
        type: 'POST',
        data: formData,
        success: function (response) 
            console.log(response);
        ,
        contentType: false,
        processData: false,
        cache: false
    );
    return false;
);
</script>

///// 其他页面.php

<?php
    print_r($_FILES);
?>

【讨论】:

以上是关于使用 Ajax 以一种形式上传数据和文件?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Ajax 以一种形式上传数据和文件?

如何在 laravel 上以一种形式使用两个控制器方法?

如何将带有附加数据的 FormData 文件发送到 asp.net web api ajax 调用

AJAX多文件上传

SpringBoot文件上传下载

在 iOS 上未选择文件时,JavaScript 使用 FormData 和 jQuery 的 ajax 上传文件返回错误 500