I'm trying to create a controller to upload photos in my MVC4 application. But I keep getting this error. The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or a non-white space character among the padding characters.
PhotosController.cs
public class PhotoController : Controller
{
public ActionResult Index()
{
using (var ctx = new BlogContext())
{
return View(ctx.Photos.AsEnumerable());
}
}
public ActionResult Upload()
{
return View(new Photo());
}
[HttpPost]
public ActionResult Upload(PhotoViewModel model)
{
var photo = Mapper.Map<PhotoViewModel, Photo>(model);
if (ModelState.IsValid)
{
PhotoRepository.Save(photo);
return RedirectToAction("Index");
}
return View(photo);
}
}
Photo.cs
public class Photo
{
public int Id { get; set; }
public Byte[] File { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string AlternateText { get; set; }
}
PhotoViewModel.cs
public class PhotoViewModel
{
public int Id { get; set; }
public HttpPostedFileBase File { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string AlternateText { get; set; }
}
/Photos/Upload.cshtml
@model Rubish.Models.Photo
@{
ViewBag.Title = "Upload";
}
<h2>Upload</h2>
@using (Html.BeginForm("Upload","Photo",FormMethod.Post,new {enctype="multipart/form-data"})) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Photo</legend>
<div class="editor-label">
@Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Description)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Description)
@Html.ValidationMessageFor(model => model.Description)
</div>
<div class="editor-label">
<label for="file">FileName:</label>
</div>
<div class="editor-field">
<input name="File" id="File" type="file"/>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@Scripts.Render("~/bundles/jqueryval")
PhotoRepository
public class PhotoRepository
{
private static BlogContext _ctx;
public PhotoRepository()
{
_ctx = new BlogContext();
}
public static void Save(Photo p)
{
_ctx.Photos.Add(p);
_ctx.SaveChanges();
}
}
The problem is that you have a property in your view model called File
which is of type byte[]
and you are also using an action parameter called file
of type HttpPostedFileBase
. The problem is that when the model binder encounters a property on your model of type byte[]
it attempts to bind its value from the request value using base64. Except that inside the request you have a multipart/form-data
encoded value of the uploaded file and you get an exception.
The correct way to fix this is to use view models instead of passing your domain models to the views:
public class PhotoViewModel
{
public HttpPostedFileBase File { get; set; }
... other properties
}
and the controller action will now become:
[HttpPost]
public ActionResult Upload(PhotoViewModel model)
{
if (ModelState.IsValid)
{
// map the domain model from the view model that the action
// now takes as parameter
// I would recommend you AutoMapper for that purpose
Photo photo = ...
// Pass the domain model to a DAL layer for processing
Repository.Save(photo);
return RedirectToAction("Index");
}
return View(photo);
}
The poor way and totally not recommended by me is to rename your file input to trick the model binder:
<input name="PhotoFile" id="File" type="file"/>
and your controller action:
[HttpPost]
public ActionResult Upload(Photo photo, HttpPostedFileBase photoFile)
{
...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With