【问题标题】:How to bind posted data named "file[]" to an MVC model?如何将名为“file[]”的发布数据绑定到 MVC 模型?
【发布时间】:2019-02-10 12:16:02
【问题描述】:

我使用Redactor 作为HTML 编辑器,它有一个component for uploading images and files

Redactor 负责客户端部分,我需要提供服务器端上传功能。

如果我在控制器中使用Request.Files,我可以正常上传。

但我想将发布的文件绑定到模型,我似乎无法做到这一点,因为它们发送的参数是files[] - 名称中带有方括号。

我的问题:

是否可以将发布的"file[]" 绑定到 MVC 模型?这是一个无效的属性名称,单独使用 file 是行不通的。


这个文件输入看起来像这样。我可以指定 file 以外的名称,但 Redactor 将 [] 添加到末尾,而不管名称如何。

<input type="file" name="file" multiple="multiple" style="display: none;">

我正在尝试绑定到这样的属性:

public HttpPostedFileBase[] File { get; set; }

当我看到上传发生时,我在请求中看到了这一点(我认为编辑器可能会在幕后添加方括号):

Content-Disposition: form-data; name="file[]"; filename="my-image.jpg"

也相关:

Redactor 总是发送内容类型为 multipart/form-data 的上传请求。所以你不需要在任何地方添加这个enctype

【问题讨论】:

  • 使用 multipart/form-data 尝试 this,可能会有所帮助。
  • 我问过,显然 Redactor 总是发送内容类型为 multipart/form-data 的上传请求。
  • DefaultModelBinder 将无法绑定它,因此您需要使用 javascript 来处理提交事件并将name 更改为file(不确定这是否可能) 或创建自定义 ModelBinder

标签: asp.net-mvc file-upload model-binding redactor


【解决方案1】:

我在一个 ASP.NET MVC 项目中集成jQuery.filer 时遇到了类似的问题。由于 jQuery.filer 在输入的名称属性末尾添加“[]”(即从文件到文件 []),我必须手动更改名称属性的值,如下所示:

$('#FileUpload').attr('name', 'FileUpload');

这是我通过 AJAX 在一些项目中使用的方法,并且工作没有任何问题。你可以试一试,让我知道它是否有效:

ViewModel:

[Display(Name = "Attachments")]
[DataType(DataType.Upload)]
public IEnumerable<HttpPostedFileBase> FileUpload { get; set; }


查看:

@model ViewModel

@using (Html.BeginForm("Insert", "Controller", FormMethod.Post, 
    new { id = "frmCreate", enctype = "multipart/form-data" })) 
{   
    @Html.TextBoxFor(m => m.FileUpload, new { type = "file", multiple = "multiple" })
    <button id="btnSubmit" onclick="insert(event)" type="button">Save</button>
}    

<script>     
function insert(event) {     
    event.preventDefault();

    //As jQuery.filer adds "[]" to the end of name attribute of input (i.e. from files to files[])
    //we have to change the value of name attribute manually
    $('#FileUpload').attr('name', 'FileUpload');        
    var formdata = new FormData($('#frmCreate').get(0)); 

    $.ajax({
        type: "POST",
        url: '@Url.Action("Insert", "Cotroller")',
        cache: false,
        dataType: "json",
        data: formdata,

        /* If you are uploading files, then processData and contentType must be set 
        to falsein order for FormData to work (otherwise comment out both of them) */
        processData: false, 
        contentType: false, 

        success: function (response, textStatus, XMLHttpRequest) {
            //...
        }
    });
};

$(document).ready(function () {         
    $('#FileUpload').filer({        
        //code omitted for brevity
    });  
});  
</script>


控制器:

public JsonResult Insert([Bind(Exclude = null)] ViewModel model)
{
    if (ModelState.IsValid)
    {   
        List<FileAttachment> fa = new List<FileAttachment>();
        if (model.FileUpload != null)
        {
            FileAttachment fileAttachment = new FileAttachment //entity model
            {
                Created = DateTime.Now,
                FileMimeType = upload.ContentType,
                FileData = new byte[upload.ContentLength],
                FileName = upload.FileName,
                AuthorId = 1
            };
            upload.InputStream.Read(fileAttachment.FileData, 0, upload.ContentLength);
            fa.Add(fileAttachment);
        }

        //code omitted for brevity
        repository.SaveExperimentWithAttachment(model, fa);
        return Json(new { success = true, message = "Record has been created." });
    }
    // If we got this far, something failed, redisplay form
    return Json(new { success = false, message = "Please check the form and try again." });
}

希望这会有所帮助...

【讨论】:

  • 感谢 Murat 的完整答案。不幸的是,尽管 Redactor 允许我指定上传参数的名称,但他们总是在其末尾添加[]。例如。我指定ImageFile,它以名称ImageFile[] 发送它们。太烦人了!
  • @MartinHansenLennox 不客气,马丁。使用 formdata.append('someProperty', 'SomeValue')ImageFile 发送到Controller 怎么样?您可以将ImageFile 数据分配给属性,并使用不带括号的新属性值在Controller 中获取此属性。
  • 谢谢。您的回答非常有帮助,我为不给您加分而感到非常难过。如果允许的话,我会平分它们。但我不得不选择,并且觉得 mohmd's 是正确的选择,因为它专门回答了发布的问题。
  • @MartinHansenLennox 没关系,因为这里最重要的是解决您的问题,正如您所说,另一个答案更具体针对您的问题。谢谢;)
【解决方案2】:

您应该创建一个自定义模型绑定器以将上传的文件绑定到一个属性。 首先创建一个具有HttpPostedFileBase[] 属性的模型

public class RactorModel
{
    public HttpPostedFileBase[] Files { get; set; }
}

然后实现DefaultModelBinder 并覆盖BindProperty

public class RactorModelBinder : DefaultModelBinder
{
    protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
    {
        int len = controllerContext.HttpContext.Request.Files.AllKeys.Length;

        if (len > 0)
        {
            if (propertyDescriptor.PropertyType == typeof(HttpPostedFileBase[]))
            {
                string formName = string.Format("{0}[]", propertyDescriptor.Name);
                HttpPostedFileBase[] files = new HttpPostedFileBase[len];
                for (int i = 0; i < len; i++)
                {
                    files[i] = controllerContext.HttpContext.Request.Files[i];
                }

                propertyDescriptor.SetValue(bindingContext.Model, files);
                return;
            }
        }

        base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
    }
}

您还应该将 binder 提供程序添加到您的项目中,然后在 global.asax 中注册它

public class RactorModenBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(Type modelType)
    {
        if (modelType == typeof(RactorModel))
        {
            return new RactorModelBinder();
        }

        return null;
    }
}
...
ModelBinderProviders.BinderProviders.Insert(0, new RactorModenBinderProvider());

这不是一个通用的解决方案,但我想你明白了。

【讨论】:

  • 感谢 Mohmd。这是我能看到使它工作的唯一方法。我会为实现而苦苦挣扎,所以这些例子真的很方便。 :)
猜你喜欢
  • 1970-01-01
  • 2017-11-21
  • 2016-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多