在ASP.Net Core MVC中使用AJAX提交表单

11

我正在使用ASP.Net Core 2.1,尝试上传文件并返回其URL,而不刷新页面。

我试图在site.js中编写JavaScript,因为_RenderPartial("scripts")会将所有脚本呈现在页面末尾,因此直接在razor视图中使用脚本标记无法正常工作。其次,将其添加到site.js中给我提供了在整个站点视图中调用脚本的机会。

我的控制器动作如下:

    [HttpPost]
    [DisableRequestSizeLimit]
    public async Task<IActionResult> Upload()
    {
      // Read & copy to stream the content of MultiPart-Form
      // Return the URL of the uploaded file
      return Content(FileName);
    }

我的视野看起来像:

<form id="FileUploadForm" action="~/Resources/Upload" method="post" enctype="multipart/form-data">
<input name="uploadfile" type="file" />
<button name="uploadbtn" type="submit" onclick="SubmitForm(this.parentElement, event)">Upload</button>

目前 site.js 的样子如下:

function SubmitForm(form, caller) {
caller.preventDefault();
$.ajax(
    {
        type: form.method,
        url: form.action,
        data: form.serialize(),
        success: function (data) { alert(data); },
        error: function (data) { alert(data); }
    })}

目前,代码绕过整个脚本,上传文件并返回显示文件名的新视图。我需要创建JavaScript的帮助。


你考虑过使用 https://www.nuget.org/packages/Microsoft.jQuery.Unobtrusive.Ajax/ 吗? - IEnjoyEatingVegetables
不是,也许你可以为此添加一个例子。 - Praveen Rai
3个回答

12

不幸的是,jQuery的serialize()方法不会包括输入文件元素。因此,用户选择的文件不会被包含在序列化值中(基本上是一个字符串)。

您可以创建一个FormData对象,并将文件附加到其中。在进行ajax调用时,您需要将processDatacontentType属性值指定为false

<form id="FileUploadForm" asp-action="Upload" asp-controller="Home" 
                                              method="post" enctype="multipart/form-data">
    <input id="uploadfile" type="file" />
    <button name="uploadbtn" type="submit">Upload</button>
</form>

这里提供一种不显眼的方式来处理表单提交事件,我们会停止默认行为,改为使用ajax方式提交。

$(function () {
    $("#FileUploadForm").submit(function (e) {
        e.preventDefault();

        console.log('Doing ajax submit');

        var formAction = $(this).attr("action");
        var fdata = new FormData();

        var fileInput = $('#uploadfile')[0];
        var file = fileInput.files[0];
        fdata.append("file", file);

        $.ajax({
            type: 'post',
            url: formAction,
            data: fdata,
            processData: false,
            contentType: false
        }).done(function (result) {
            // do something with the result now
            console.log(result);
            if (result.status === "success") {
                alert(result.url);
            } else {
                alert(result.message);
            }
        });
    });
})

假设您的服务器端方法有一个与我们在创建FormData对象时使用的名称相同的参数(file)。下面是一个示例,它将上传图像到wwwwroot内的uploads目录中。

该操作方法返回一个JSON对象,其中包含状态和url/消息属性。您可以在ajax调用的success/done处理程序中使用它来执行您想要的操作。

public class HomeController : Controller
{
    private readonly IHostingEnvironment hostingEnvironment;
    public HomeController(IHostingEnvironment environment)
    {
        _context = context;
        hostingEnvironment = environment;
    }
    [HttpPost]
    public async Task<IActionResult> Upload(IFormFile file)
    {
        try
        {
            var uniqueFileName = GetUniqueFileName(file.FileName);
            var uploads = Path.Combine(hostingEnvironment.WebRootPath, "uploads");
            var filePath = Path.Combine(uploads, uniqueFileName);
            file.CopyTo(new FileStream(filePath, FileMode.Create));
            var url = Url.Content("~/uploads/" + uniqueFileName);
            return Json(new { status = "success", url = url });
        }
        catch(Exception ex)
        {
            // to do : log error
            return Json(new { status = "error", message = ex.Message });
        }
    }
    private string GetUniqueFileName(string fileName)
    {
        fileName = Path.GetFileName(fileName);
        return Path.GetFileNameWithoutExtension(fileName)
                  + "_"
                  + Guid.NewGuid().ToString().Substring(0, 4)
                  + Path.GetExtension(fileName);
    }
}

选择器$('#uploadfile')[0]返回null。为了使此函数在整个网站中可用,我正在传递表单的实例。如何获取表单中的所有文件输入元素? - Praveen Rai
参考 https://developer.mozilla.org/zh-CN/docs/Web/API/FormData/FormData :FormData 在所有浏览器上似乎并未完全实现。有什么替代方案吗? - Praveen Rai
如果$('#uploadfile')[0]返回null,那么意味着你的文件元素没有一个Id为uploadfile(检查答案中的标记)。更新你的jQuery选择器以匹配你的标记。如果你想发送多个文件,使用更通用的jQuery选择器(例如:名称/类)并使用循环将其添加到FormData对象中。更新你的服务器端代码以接受一个IFormFile集合。哪个具体的浏览器不支持它? - Shyju
谢谢@Shyju!终于让它运行起来了,也为了参考而发布我的代码。只是一个问题,我尝试将表单作为参数传递给FormData构造函数,但似乎不起作用。我参考了这篇文章:https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData#Syntax - Praveen Rai
关于浏览器支持,Mozilla API文档表示iOS Safari和三星Internet的兼容性状态未知。这让我相信FormData API的实现可能仍在进行中。 - Praveen Rai

1

我分享了我使用的代码,实现了@Shyju的答案。

视图(Razor页面):

<form name="UploadForm" action="~/Resources/Upload" method="post" enctype="multipart/form-data">
<input name="uploadfile" type="file" />
<button name="uploadbtn" type="submit" onclick="SubmitForm(this.parentElement, event)">Upload</button>

在 Site.js 中添加的 AJAX 代码(使其可重用):

// The function takes Form and the event object as parameter
function SubmitForm(frm, caller) {
caller.preventDefault();

var fdata = new FormData();

var file = $(frm).find('input:file[name="uploadfile"]')[0].files[0];
fdata.append("file", file);

$.ajax(
    {
        type: frm.method,
        url: frm.action,
        data: fdata, 
        processData: false,
        contentType: false,
        success: function (data) {
            alert(data);
        },
        error: function (data) {
            alert(data);
        }
    })

};


0
如果您想在不使用Ajax请求的情况下提交表单
var form = document.getElementById('formId');

form.submit();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接