如何在ASP.NET MVC中保持表单的异步上载文件?

Joe*_*Min 1 asp.net ajax asp.net-mvc jquery asynchronous

这是场景:

  1. 我有一个使用ASP.NET MVC开发的网站,该网站中有一个表单,其中包含多个输入字段,其中包括几个文件上传字段。

  2. 我正在尝试做的是,一旦用户选择了文件,就将文件 异步上传,以便在文件上传时,用户可以继续填写其他输入。

  3. 服务器接收 验证文件有效后,当用户单击“提交”按钮时,表单将与上载的文件一起发送并保存。

我假设我需要有一个单独的控制器操作来处理异步上传,但是那我将如何从一个处理表单提交不同操作(在同一控制器内)访问这些上传文件呢?

如果我需要在提交表单之前临时存储文件,那么理想的存储位置在哪里?

小智 5

如果您在选择表单时已经上传了文件,那么在提交表单时上传文件似乎毫无意义(您可能希望enctype从表单元素中删除该属性。

基本步骤是当用户使用ajax选择文件时使用(上载)上载文件FormData,保存文件并返回该文件的路径,以便最终提交表单时也可以发布该信息。

首先定义视图模型以表示要在视图中显示/编辑的内容

public class FileAttachmentVM
{
    public string Path { get; set; }
    public string DisplayName { get; set; }
}
public class MyViewModel
{
    .... // properties to display/edit in the view
    public List<FileAttachmentVM> Files { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

并认为

@model MyViewModel
...
@using (Html.BeginForm())
{
    ....
    <div id="file-inputs"></div> // placeholder for the file collection hidden inputs
}
<div id="file-names"></div> // place holder for the collection of file already uploaded
<input type="file" id="file" />
<button type="button" id="upload">Upload</button>
// template for adding new inputs for the file collection
<div id="template" style="display:none">
    <div class="file-details">
        <input type="hidden" class="file-path" name="Files[#].Path" value="" />
        <input type="hidden" class="file-name" name="Files[#].DisplayName" value="" />
        <input type="text" name="Files.Index" value="#" />
    </div>
</div>
Run Code Online (Sandbox Code Playgroud)

以及用于上传文件和更新DOM的脚本

var fileInputs = $('#file-inputs');
var fileNames = $('#file-names');

$('#Upload').click(function () {
    var file = $('#file').get(0).files[0];
    if (!file) {
        return;
    }
    var formData = new FormData();
    formData.append("file", file);
    $.ajax({
        url: '@Url.Action("Upload", "...")',// add controller name
        type: 'POST',
        data: formData,
        processData: false,
        contentType: false,
        success: function (data) {
            if (!data) {
                // Oops, something went wrong
                return;
            }
            $('#file').val(''); // clear file input
            // Add the display name
            fileNames.append($('<div></div>').text(data.DisplayName));
            // Add the inputs
            var index = (new Date()).getTime(); // unique indexer
            var clone = $('#template').clone(); // clone the template
            clone.html($(clone).html().replace(/#/g, index)); // update the indexer
            fileInputs.append(clone.html()); // append the inputs
            // update the input values
            var lastFile = fileInputs.find('.file-details').last();
            lastFile.find('.file-path').last().val(data.Path);
            lastFile.find('.file-name').last().val(data.DisplayName);
        }
    });
});
Run Code Online (Sandbox Code Playgroud)

最后是控制器方法

[HttpPost]
public JsonResult Upload(HttpPostedFileBase file)
{
    if (file != null && file.ContentLength > 0)
    {
        var displayName = Path.GetFileName(file.FileName);
        var fileExtension = Path.GetExtension(displayName);
        var fileName = string.Format("{0}{1}", Guid.NewGuid(), fileExtension);
        var path = Path.Combine(Server.MapPath("~/App_Data/Files"), fileName);
        file.SaveAs(path);
        return Json(new { DisplayName = displayName, Path = path });
    }
    return Json(null);
}
Run Code Online (Sandbox Code Playgroud)

然后在控制器方法中将表单提交到

public ActionResult Create(MyViewModel model)
{
    // Initialize a data model, map the view model properties to it and save
    // Loop through model.Files and save each file display name and path to the database (along with the ID of the entity you created/updated
Run Code Online (Sandbox Code Playgroud)

有关代码的一些注释。

  1. 您表示希望用户选择文件后立即上传文件。IMO,这不是预期的行为,最好允许用户明确检查然后单击上载按钮,但是您可以修改脚本以处理文件输入.change() 事件而不是按钮.click()事件。
  2. 这些文件Guid用来保存,以确保它们是唯一的,并且如果另一个用户上传具有相同名称的文件,则不会存在文件被覆盖的风险。
  3. 您需要考虑如果用户上载了一些文件但又放弃了编辑该怎么办。例如,您可以将它们保存到一个临时目录中,然后以Submit方法的形式将它们移动到一个永久目录中(并定期删除所有废弃的文件)
  4. 您可能还希望在显示名称旁边添加一个“删除”链接,以防用户改变主意(该链接将调用ajax方法来删除文件)。