I want to upload a file and send along with the file some additional information, let's say a string foo and an int bar.
How would I write a ASP.NET WebAPI controller method that receives a file upload, a string, and an int?
My JavaScript:
var fileInput = document.querySelector("#filePicker");
var formData = new FormData();
formData.append("file", fileInput.files[0]);
formData.append("foo", "hello world!");
formData.append("bar", 42);
var options = {
url: "/api/foo/upload",
data: formData,
processData: false // Prevents JQuery from transforming the data into a query string
};
$.ajax(options);
My WebAPI controller can access the file like this:
public async Task<HttpResponseMessage> Upload()
{
var streamProvider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(streamProvider);
var fileStream = await streamProvider.Contents[0].ReadAsStreamAsync();
}
But it's not clear to me how I can get at my string and my int. I figure I can probably say streamProvider.Content[1], or whatever, but that feels super nasty.
What's the Right Way© to write a WebAPI action that accepts a file upload, a string, and an int?
The rules are the same as those applied to the set metadata values REST API. Use Content-Type: application/json to describe this information as a JSON object. File to upload. This must come after the jsonInputParameters parameter.
As mentioned, Web API controller can include multiple Get methods with different parameters and types. Let's add following action methods in StudentController to demonstrate how Web API handles multiple HTTP GET requests.
You can create your own MultipartFileStreamProvider
to access the additional arguments.
In ExecutePostProcessingAsync
we loop through each file in multi-part form and load the custom data (if you only have one file you'll just have one object in the CustomData
list).
class MyCustomData
{
public int Foo { get; set; }
public string Bar { get; set; }
}
class CustomMultipartFileStreamProvider : MultipartMemoryStreamProvider
{
public List<MyCustomData> CustomData { get; set; }
public CustomMultipartFileStreamProvider()
{
CustomData = new List<MyCustomData>();
}
public override Task ExecutePostProcessingAsync()
{
foreach (var file in Contents)
{
var parameters = file.Headers.ContentDisposition.Parameters;
var data = new MyCustomData
{
Foo = int.Parse(GetNameHeaderValue(parameters, "Foo")),
Bar = GetNameHeaderValue(parameters, "Bar"),
};
CustomData.Add(data);
}
return base.ExecutePostProcessingAsync();
}
private static string GetNameHeaderValue(ICollection<NameValueHeaderValue> headerValues, string name)
{
var nameValueHeader = headerValues.FirstOrDefault(
x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
return nameValueHeader != null ? nameValueHeader.Value : null;
}
}
Then in your controller:
class UploadController : ApiController
{
public async Task<HttpResponseMessage> Upload()
{
var streamProvider = new CustomMultipartFileStreamProvider();
await Request.Content.ReadAsMultipartAsync(streamProvider);
var fileStream = await streamProvider.Contents[0].ReadAsStreamAsync();
var customData = streamProvider.CustomData;
return Request.CreateResponse(HttpStatusCode.Created);
}
}
I think the answers here are excellent. So others can see a somewhat simple example of how to pass data in addition to the file in summary form, included is a Javascript Function that makes the WebAPI call to the FileUpload Controller, and the snippet from the FileUpload Controller (in VB.net) that reads the additional data passed from Javascript.
Javascript:
function uploadImage(files) {
var data = new FormData();
if (files.length > 0) {
data.append("UploadedImage", files[0]);
data.append("Source", "1")
var ajaxRequest = $.ajax({
type: "POST",
url: "/api/fileupload/uploadfile",
contentType: false,
processData: false,
data: data
});
File Upload Controller:
<HttpPost> _
Public Function UploadFile() As KeyValuePair(Of Boolean, String)
Try
If HttpContext.Current.Request.Files.AllKeys.Any() Then
Dim httpPostedFile = HttpContext.Current.Request.Files("UploadedImage")
Dim source = HttpContext.Current.Request.Form("Source").ToString()
So as you can see in the Javascript, the additional data passed is the "Source" key, and the value is "1". And as Chandrika has answered above, the Controller reads this passed data through "System.Web.HttpContext.Current.Request.Form("Source").ToString()".
Note that Form("Source") uses () (vs. []) as the controller code is in VB.net.
Hope this helps.
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