I am trying to upload attachments for an email message that will be sent from my site. I'm using an input tag of type file with the multiple keyword specified, and ajax to send the file(s) and other model properties back to the controller.
However, all the model properties are bound once the controller is reached, with the exception of the property to contain the files.
The uploaded file(s) are present when the request is inspected with fiddler and they are also present in the Request.Files collection on the controller.
Should my files be automatically bound to the model using this method, or do I have to write a custom binder to accomplish this?
[Non relevant code has been removed]
This is the object that should contain the files
public class Attachment
{
public IEnumerable<HttpPostedFileBase> Files { get; set; }
public Attachment()
{
Files = new List<HttpPostedFileBase>();
}
}
This is the object that contains the attachments and the other model properties
public class QuoteRequest
{
public Attachment Attachment { get; set; }
public QuoteRequest()
{
Attachment = new Attachment();
}
}
The view the user will use to upload has the following form
@using (Html.BeginForm("Index", "Quote", FormMethod.Post, new { required = "true", id = "frm-quote", enctype = "multipart/form-data" })) {
@Html.AntiForgeryToken()
@Html.EditorFor(model => model.Contact, "ContactForm")
@Html.EditorFor(model => model.Enquiry, "EnquiryForm")
@Html.EditorFor(model => model.Attachment, "AttachmentForm")
<div class="row">
<div class="col l10 offset-l2 m6 s12 add-pad-bot center">
<button class="col s12 btn waves-effect orange" type="reset" data-test-clear>CLEAR</button>
</div>
<div class="col l10 offset-l2 m6 s12 center">
<button class="col s12 btn waves-effect waves-green green" type="button" data-test-submit>SUBMIT</button>
</div>
</div> }
This is the partial view that deals with the file input tag
@using Domain.Models
@model Attachment
<section id="attachment" class="row no-mar-bot">
<div class="file-field input-field col s12">
<div class="btn primary">
<span id="icon-attachment">
<i class="medium material-icons">note_add</i>
</span>
@Html.TextBoxFor(m => m.Files, new {type = "file", multiple =
"multiple"})
</div>
<div class="file-path-wrapper">
<input class="file-path validate" type="text" placeholder="Upload one or more files">
</div>
</div>
</section>
The javascript function that gets the form inputs to post to the controller
function getFormData() {
var formData;
formData = new FormData();
var fileUpload = $("#Attachment_Files").get(0);
var files = fileUpload.files;
if (files) {
// Looping over all files and add it to FormData object
for (var i = 0; i < files.length; i++) {
console.log("files[i].name:" + files[i].name);
formData.append(files[i].name, files[i]);
}
}
// You can update the jquery selector to use a css class if you want
$("input:not([type='file']), textarea").each(function (x, y) {
formData.append($(y).attr("name"), $(y).val());
});
return formData; }
The code for the ajax post
$.ajax({
url: "/quote",
method: "POST",
cache: false,
contentType: false,
processData: false,
data: getFormData()
headers: {
'__RequestVerificationToken':
$("input[name='__RequestVerificationToken']").val()
}
})
.fail(function(jqXHR, textStatus) { })
.done(function(data) { });
This is the controller action method that receives the post
[HttpPost]
[ValidateAntiForgeryHeader]
[Route("")]
public ActionResult Index(QuoteRequest model) { }