2

I am required to integrate a signature pad into an intranet (MVC4) application allowing people to apply electronic signatures to system generated documents. Unfortunately, the signature pad I've been given only has a COM/ActiveX API, so I've written a short Windows Forms application that will allow the user to capture the signature and upload it to the server. When it is uploaded, I need the MVC4 action to associate the signature image with a specified document entity sent by the Windows Forms request. So, say I have this model:

public class DocumentToSign { 
    public int DocumentId { get; set; }
    public int DocumentTypeId { get; set; } 
}

Then I have this action to receive the uploaded image:

[HttpPost]
public ActionResult UploadSignature(DocumentToSign doc, HttpPostedFileBase signature)
{
    //do stuff and catch errors to report back to winforms app
    return Json(new {Success = true, Error = string.Empty});
}

Then, the code to upload the image:

var doc = new DocumentToSign{ DocumentId = _theId, DocumentTypeId = _theType };
var fileName = SaveTheSignature();
var url = GetTheUrl();
using(var request = new WebClient())
{
    request.Headers.Add("enctype", "multipart/form-data");
    foreach(var prop in doc.GetType().GetProperties())
    {
        request.QueryString.Add(prop.Name, Convert.ToString(prop.GetValue(doc, null)));
    }
    var responseBytes = request.UploadFile(url, fileName);
    //deserialize resulting Json, etc.
}

The model binder seems to pick up the DocumentToSign class without any problems, but the HttpPostedFileBase is always null. I know that I need to somehow tell the model binder that the uploaded image is the signature parameter in the action, but I can't figure out how to do it. I tried using UploadValues with a NameValueCollection, but NameValueCollection only allows the value to be a string, so the image (even as a byte[]) can't be part of that.

Is it possible to upload a file as well as a model to the same action from outside of the actual MVC4 application? Should I be using something other than HttpPostedFileBase? Other than the WebClient? I am at a loss.

AJ.
  • 16,368
  • 20
  • 95
  • 150
  • The `signature` parameter will be bound to a value in the request of the same name, for example an input with `name="signature"` or a querystring parameter named `signature`. – asymptoticFault Aug 26 '13 at 18:06
  • With the `byte[]` for the file, you're going to have to add that to the posted data. You won't be able to use `UploadFile`, you simply need to make a `POST` to that URL with the file in the form. – Mike Perrenoud Aug 26 '13 at 18:07
  • It is absolutely possible to have a model and additional fields populated in the same action method. – asymptoticFault Aug 26 '13 at 18:07
  • @asymptoticFault - yes, I have done it several times using a form and an ``, but have never from outside of the application. – AJ. Aug 26 '13 at 18:34

1 Answers1

1

var responseBytes = request.UploadFile(url, fileName); is not sending your file in the format your controller expect. HttpPostedFileBase works with multipart/form-data POST request. But WebClient.UploadFile is not sending a multipart request, it sends file content as a body of request with no other information. You can save the file by calling Request.SaveAs(filename, false); or you have to change the way you are sending the file. But I don't think WebClient support sending multipart requests.

Andrey M.
  • 3,688
  • 3
  • 33
  • 36
  • OK, that makes sense. So I can't use request.UploadFile. If `WebClient` won't handle it, do you know what will? – AJ. Aug 26 '13 at 18:36
  • `System.Net.Http.HttpClient` can. Here is an example http://stackoverflow.com/questions/16416601/c-sharp-httpclient-4-5-multipart-form-data-upload – Andrey M. Aug 26 '13 at 18:43
  • So it looks like I have to change the signature of my controller action to do the async thing, right? I have it posting using `HttpClient` but the controller still isn't getting anything. – AJ. Aug 26 '13 at 19:53
  • Then you probably need to debug the problem. Set a breakpoint inside controller and check if Request.Files contains any files and that the key value is the same as the name of the parameter of your controller. – Andrey M. Aug 26 '13 at 19:58
  • In the end, I forgot that my MVC4 application is targeting 4.0 instead of 4.5, so I can't go the `HttpClient` route. I wound up using boundaries, with something like this: http://aspnetupload.com/Upload-File-POST-HttpWebRequest-WebClient-RFC-1867.aspx. Thanks, though. I think this is the answer for 4.5 applications. – AJ. Aug 27 '13 at 13:45