I'm very new to KnockoutJS, and I dig it so far, but try as I might I couldn't find this info anywhere so I am hoping the community can help! In my View I have the following data-binding on a file input:
<input type="file" data-bind="value: ImageToUpload"/>
<button data-bind="click: $root.saveImage">Upload</button>
This is part of a list in a "foreach" div, so the variable "ImageToUpload" corresponds to a property on an object from that list.
In my ViewModel the Upload button calls saveImage() and I call the web service and pass the form data to a .aspx page:
self.saveImage = function (MyObject, event) {
$.post("Service.aspx", MyObject, function (returnedData) {
});
}
The object passes to my service just fine and I can access all the form data as expected including the "ImageToUpload" variable... but here is where I am stuck:
1) The "ImageToUpload" is only a string representing the name of the file I uploaded and is not a ByteArray. How do I access the image file and not just the name?
2) Is there a better way to pass the ByteArray as a Stream or other format in the response header?
3) Is my technique totally off? Is there a better way to do this? My goal is to have a dynamic list of image "slots" that be uploaded to.
Thanks in advance!
Inability to access local file contents was a fundamental limitation of JavaScript security sandbox. It was lifted with the introduction of HTML5 file API, but, unfortunately, support is far from universal. If your code needs to work in non-compliant browsers, your only option is to let the browser handle the traditional multipart/form-data
upload. If, on the other hand, not supporting IE < 10 is fine for you, it is possible to use File API "the Knockout way" with custom bindings. Have a look at this example: http://khayrov.github.com/jsfiddle/knockout-fileapi (for some reason jsFiddle breaks badly for me when I try to run this code in it). Source code at Github.
File uploads need to be handled in a special manner. They are generally not part of your model. Take a look at the following ApiController. Specialized classes such as the MultipartFormDataStreamProvider
are used to access the binary data that was submitted to the controller.
public class FileUploadController : ApiController
{
[HttpPost]
public List<FileResult> UploadFile()
{
// Verify that this is an HTML Form file upload request
if (!Request.Content.IsMimeMultipartContent("form-data"))
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
// Create a stream provider for setting up output streams
MultipartFormDataStreamProvider streamProvider = new MultipartFormDataStreamProvider();
// Read the MIME multipart content using the stream provider we just created.
IEnumerable<HttpContent> bodyparts = Request.Content.ReadAsMultipartAsync(streamProvider).Result;
// The submitter field is the entity with a Content-Disposition header field with a "name" parameter with value "submitter"
string submitter;
if (!bodyparts.TryGetFormFieldValue("submitter", out submitter))
{
submitter = "unknown";
}
// Get a dictionary of local file names from stream provider.
// The filename parameters provided in Content-Disposition header fields are the keys.
// The local file names where the files are stored are the values.
IDictionary<string, string> bodyPartFileNames = streamProvider.BodyPartFileNames;
// Create response containing information about the stored files.
return bodyPartFileNames.Select(kv =>
{
FileInfo fileinfo = new FileInfo(kv.Value);
return new FileResult
{
FileName = kv.Key,
LocalPath = fileinfo.FullName,
LastModifiedTime = fileinfo.LastWriteTimeUtc,
Length = fileinfo.Length,
Submitter = submitter
};
}).ToList();
}
private static bool TryGetFormFieldValue(IEnumerable<HttpContent> contents, string dispositionName, out string formFieldValue)
{
HttpContent content = contents.FirstDispositionNameOrDefault(dispositionName);
if (content != null)
{
formFieldValue = content.ReadAsStringAsync().Result;
return true;
}
formFieldValue = null;
return false;
}
}
An alternative solution that's a little less heavy on the code is to use HttpPostedFileBase
as a parameter for your ApiController. HttpPostedFileBase
is essentially a wrapper for a file that was uploaded through a form. I must add that using MultipartFormDataStreamProvider
is preferred over HttpPostedFileBase
, because the latter is part of the ASP.NET MVC framework. Remember, WebAPI technology can be used outside of ASP.NET MVC.
More info: http://blogs.msdn.com/b/henrikn/archive/2012/03/01/file-upload-and-asp-net-web-api.aspx and ASP.NET Web API File saved as "BodyPart_3ded2bfb-40be-4183-b789-9301f93e90af"
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