1

Anyone managed to get default binder to work with input file control and property of type byte array?

If I have a property on my ViewModel named Image and a file input control on my view name Image, default binder issue 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.

ucMedia
  • 4,105
  • 4
  • 38
  • 46
mare
  • 13,033
  • 24
  • 102
  • 191

2 Answers2

3

Why do you need a byte[] array? The default model binder works with HttpPostedFileBase:

<% using (Html.BeginForm("upload", "home", FormMethod.Post, new { enctype = "multipart/form-data" })) { %>
    <input type="file" name="file" id="file" />
    <input type="submit" value="Upload" />
<% } %>

And the controller action that will handle this:

[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
    if (file.ContentLength > 0) 
    {
        var fileName = Path.GetFileName(file.FileName);
        var path = Path.Combine(Server.MapPath("~/App_Data"), fileName);
        file.SaveAs(path);
    }
    return RedirectToAction("Index");
}

This works also with multiple files. You simply use IEnumerable<HttpPostedFileBase> in the signature of the action method.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • My actions have a view model in their signature and a HttpPostedFileBase and my view model class contains a byte[] public property. I then copy the contents of HttpPostedFileBase to my byte[] aray. Why? Because I cannot have HttpPostedFileBase as a type for my property because I am doing serialization for persistance. Even if used some DB for storing data I would still need to put it into byte array to be able to save it in DB as Image type. So I was wondering if the default binder can automatically bind to byte[] array. – mare Jul 21 '10 at 09:02
  • To make it easier for you to understand what I am doing, let's just say that I almost never save uploaded files to disk like you provided an example for. If there's a file uploaded it gets saved to XML or DB. – mare Jul 21 '10 at 09:04
  • You should be using ViewModels which are close to the Views and contain properties such as `HttpPostedFileBase`. Those ViewModels should not be persisted. You should have a mapping between those ViewModels and the business model that might contain `byte[]` if you will. Also the save to file was just an example. You could call your repository by passing it the `byte[]` which will take care of storing it in a database (As a side note I don't think it is a good idea to store uploaded files into a SQL database). – Darin Dimitrov Jul 21 '10 at 09:34
  • 1
    Yeah that's how I was doing so far - using HttpPostedFileBase and manually copying it over to byte[], I just thought if there's another way. I actually like storing files in the DB - that way all the content is in one place, DB gets backed up and is fully restorable along with files, files are not exposed to sysadmin's (and potential others) on the filesystem so they can't get accidentally deleted or modified in any way and you can forget about them. We had huge problems when migrating sites that had files on filesystem and they forgot to migrate them. – mare Jul 21 '10 at 09:57
1

You should create a custom model binder that associates directly your uploaded file to a byte[] field in your model.

Example:

public class CustomByteArrayModelBinder : ByteArrayModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var file = controllerContext.HttpContext.Request.Files[bindingContext.ModelName];

        if (file != null)
        {
            if (file.ContentLength > 0)
            {
                var fileBytes = new byte[file.ContentLength];
                file.InputStream.Read(fileBytes, 0, fileBytes.Length);
                return fileBytes;
            }

            return null;
        }

        return base.BindModel(controllerContext, bindingContext);
    }
}

You also have to remove the default model binder, and add yours (in Global.asax.cs, inside the Application_Start method):

ModelBinders.Binders.Remove(typeof(byte[]));
ModelBinders.Binders.Add(typeof(byte[]), new CustomByteArrayModelBinder());

This code were retired from this good article: http://prideparrot.com/blog/archive/2012/6/model_binding_posted_file_to_byte_array

Best regards :)