The Components
- A form with some values on an asp.net web form
- A file input (standard HTML5, not asp:File or anything like that)
- The bluimp jQuery file uploader which I understand is pretty great for this sort of thing but have never used
- An MVC4 WebAPI controller
- A knockoutJS viewmodel in the web form's JavaScript (shouldn't matter here but worth mentioning).
The Goal
Basically, I have tiny Excel sheets that I'm uploading to validate, process, turn into business objects, and return as JSON items.
I'd like the following to happen:
- User types in some data to the form, etc.
- User adds a file to the form.
- The file is automatically uploaded (using bluimp control) to the MVC controller along with a form field value that contains text.
Next, on the controller,
- the text field passed in is sanitized/validated.
- the file is processed into memory
- (these are truly tiny and there aren't many of them so I'm thinking we shouldn't need to go to the file system?)
- The file is then processed using a
FileProcessor
that uses the stream and is instantiated using the text field.
The Problem
I don't understand these components well enough yet to understand if I'm making a simple mistake aor a complex one.
What I Have so Far
NOTE: Was trying this with a button to explicitly click to send the file, though in the future I'd like to do it automatically.
The form to submit the file:
<form id="fileUploadForm" action="/api/InvoiceDetailsFile/PostForProcessing" method="POST" enctype="multipart/form-data">
<input type ="hidden" name="clientSiteId" id="clientSiteId" value="3"/>
<input type="file" id="inpFile" required="required"/>
<input type ="button" id="btnSendFile" title="Send File" value="Send File"/>
</form>
The JavaScript for the Upload (this is likely very wrong):
$(document).ready(function () {
'use strict';
var btnSendFile = $("#btnSendFile");
var frmFileUpload = $("#fileUploadForm");
btnSendFile.click(function () {
console.log('send button clicked');
frmFileUpload.fileupload("send");
});
});
The WebAPI Controller to handle the upload (this is likely very wrong):
[HttpPost]
public Task<List<IInvoiceDetailItem>> PostForProcessing(int clientSiteId)
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var results = new List<IInvoiceDetailItem>();
var streamProvider = new MultipartMemoryStreamProvider();
var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith(t =>
{
// can I only expect 1 item here?
foreach (var item in streamProvider.Contents)
{
// if it's an Excel Sheet
if (item.IsMimeMultipartContent("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
{
try {
var workbook = new XLWorkbook(item.ReadAsStreamAsync().Result);
// clientSiteId here comes from form value (via constructor?), ContextUser comes from a BaseApi.
var processor =
new InvoiceProcessorFactory(clientSiteId, ContextUser)
.GetInvoiceProcessorInstance();
results = processor.ProcessInvoice(workbook).ToList();
}
catch (Exception ex)
{
throw new HttpException(Convert.ToInt32(HttpStatusCode.InternalServerError), "Well, this is a bummer. The invoice processor failed. Please see the inner exception.", ex);
}
}
}
return results;
});
return task;
}
I'll continue to update the code samples as I progress but I wanted to get it out there sooner than later since I know I'll be struggling with it.
Post
attribute on a Web API action...System.Net.Http.HttpClient
to uploading multipart form data content...TheSystem.Net.Http
library has a content calledMultipartFormDataContent
through which you can simulate a request how a browser would do..this way you can figure out if your server code is good or not...