File Upload with Additonal Form Data to Web Api from MVC
asp.net-mvc-4, asp.net-web-api, file-upload, multipartform-data
Solution
Finally i resolved my problem and it was all about async :)
As you can see at API action method i had called `ReadAsMultipartAsync` method synchrously but this was a mistake. I had to call it with `ContinueWith` so after i changed my code like below my problem solved.
var files = Request.Content.ReadAsMultipartAsync(provider).ContinueWith<HttpResponseMessage>(task => {
if(task.IsFaulted)
throw task.Exception;
// do additional stuff
});
Problem
I am trying to upload a file with additional form data and post to Web API via MVC but i couldn't accomplish. MVC Side Firstly i got the submitted form at MVC. Here is the action for this. ``` [HttpPost] public async Task<ActionResult> Edit(BrandInfo entity) { try { byte[] logoData = null; if(Request.Files.Count > 0) { HttpPostedFileBase logo = Request.Files[0]; logoData = new byte[logo.ContentLength]; logo.InputStream.Read(logoData, 0, logo.ContentLength); entity.Logo = logo.FileName; entity = await _repo.Update(entity.BrandID, entity, logoData); } else entity = await _repo.Update(entity,entity.BrandID); return RedirectToAction("Index", "Brand"); } catch(HttpApiRequestException e) { // logging, etc return RedirectToAction("Index", "Brand"); } } ``` Below code post the Multipartform to Web API ``` string requestUri = UriUtil.BuildRequestUri(_baseUri, uriTemplate, uriParameters: uriParameters); MultipartFormDataContent formData = new MultipartFormDataContent(); StreamContent streamContent = null; streamContent = new StreamContent(new MemoryStream(byteData)); streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") { FileName = "\"" + fileName + "\"", Name = "\"filename\"" }; streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); formData.Add(streamContent); formData.Add(new ObjectContent<TRequestModel>(requestModel, _writerMediaTypeFormatter), "entity"); return _httpClient.PutAsync(requestUri, formData).GetHttpApiResponseAsync<TResult>(_formatters); ``` As you can see i am trying to send file data and object with same `MultipartFormDataContent`. I couldn't find better way to send my entity as `ObjectContent`. Also i am using JSON.Net Serializer Regarding to fiddler, post seems successfull. ``` PUT http://localhost:12836/api/brand/updatewithlogo/13 HTTP/1.1 Content-Type: multipart/form-data; boundary="10255239-d2a3-449d-8fad-2f31b1d00d2a" Host: localhost:12836 Content-Length: 4341 Expect: 100-continue --10255239-d2a3-449d-8fad-2f31b1d00d2a Content-Disposition: form-data; filename="web-host-logo.gif"; name="filename" Content-Type: application/octet-stream GIF89a��L������X�������wW����������xH�U�)�-�k6�������v6�������̥�v�J���������7����V:�=#�ի�I(�xf�$������� // byte data // byte data 'pf�Y��y�ؙ�ڹ�(�; --10255239-d2a3-449d-8fad-2f31b1d00d2a Content-Type: application/json; charset=utf-8 Content-Disposition: form-data; name=entity {"BrandID":13,"AssetType":null,"AssetTypeID":2,"Logo":"web-host-logo.gif","Name":"Geçici Brand","Models":null,"SupplierBrands":null} --10255239-d2a3-449d-8fad-2f31b1d00d2a-- ``` Web API Side Finally i am catching post at Web API side and trying to parse but i couldn't. Because `MultipartFormDataStreamProvider`'s `FileData` and `FormData` collections are allways empty. ``` [HttpPut] public void UpdateWithLogo(int id) { if(Request.Content.IsMimeMultipartContent()) { var x = 1; // this code has no sense, only here to check IsMimeMultipartContent } string root = HttpContext.Current.Server.MapPath("~/App_Data"); var provider = new MultipartFormDataStreamProvider(root); try { // Read the form data. Request.Content.ReadAsMultipartAsync(provider); foreach(var key in provider.FormData.AllKeys) { foreach(var val in provider.FormData.GetValues(key)) { _logger.Info(string.Format("{0}: {1}", key, val)); } } // This illustrates how to get the file names. foreach(MultipartFileData file in provider.FileData) { _logger.Info(file.Headers.ContentDisposition.FileName); _logger.Info("Server file path: " + file.LocalFileName); } } catch(Exception e) { throw new HttpApiRequestException("Error", HttpStatusCode.InternalServerError, null); } } ``` I hope you can help to find my mistake. UPDATE I also realized that, if i comment out `StreamContent` or `ObjectContent` and only add StringContent, still i can't get anything from `MultipartFormDataStreamProvider`.