It turns out I was simply calling the wrong overload of MultipartFormDataContent.Add (see comment in client portion below).
Client:
var client = new HttpClient() ;
var formData = new MultipartFormDataContent();
/// FileContent code from stackoverflow.com/questions/17497584/cant-post-a-file-to-asp-net-web-api-from-httpclient
formData.Add( new FileContent( @"C:\temp\killme.xml" ), "name_not_used", "killme.xml" ); // third param causes provider.FileData on server to be populated.
formData.Add( new ObjectContent<MyCustomObject>( new MyCustomObject{ Path=@"C:\temp\killme.xml" }, new JsonMediaTypeFormatter() ), "options" );
client.PostAsync( "http://localhost:52800/api/Import", formData ).ContinueWith( ( task ) =>
{
Console.WriteLine( task.Result.StatusCode.ToString() );
client.Dispose();
} );
var formData = new MultipartFormDataContent();
/// FileContent code from stackoverflow.com/questions/17497584/cant-post-a-file-to-asp-net-web-api-from-httpclient
formData.Add( new FileContent( @"C:\temp\killme.xml" ), "name_not_used", "killme.xml" ); // third param causes provider.FileData on server to be populated.
formData.Add( new ObjectContent<MyCustomObject>( new MyCustomObject{ Path=@"C:\temp\killme.xml" }, new JsonMediaTypeFormatter() ), "options" );
client.PostAsync( "http://localhost:52800/api/Import", formData ).ContinueWith( ( task ) =>
{
Console.WriteLine( task.Result.StatusCode.ToString() );
client.Dispose();
} );
Server:
public HttpResponseMessage Post()
{
if ( Request.Content.IsMimeMultipartContent() )
{
string root = HttpContext.Current.Server.MapPath( "~/App_Data" ); // This call can't be in async portion.
var provider = new MultipartFormDataStreamProvider( root );
try
{
Request.Content.ReadAsMultipartAsync( provider ).ContinueWith( task =>
{
var options = JsonConvert.DeserializeObject<MyCustomObject>( provider.FormData["options"] );
if ( !task.IsFaulted )
{
foreach ( MultipartFileData file in provider.FileData ) {
Debug.Print( file.Headers.ContentDisposition.FileName ); // file name parameter to formData.Add back on client.
Debug.Print( "Server file path: " + file.LocalFileName ); // posted file automatically streamed to path specified by root var above.
}
}
} );
}
catch ( Exception e )
{
; // TBD
}
return new HttpResponseMessage( HttpStatusCode.Accepted );
}
else
{
return new HttpResponseMessage( HttpStatusCode.BadRequest );
}
}
{
if ( Request.Content.IsMimeMultipartContent() )
{
string root = HttpContext.Current.Server.MapPath( "~/App_Data" ); // This call can't be in async portion.
var provider = new MultipartFormDataStreamProvider( root );
try
{
Request.Content.ReadAsMultipartAsync( provider ).ContinueWith( task =>
{
var options = JsonConvert.DeserializeObject<MyCustomObject>( provider.FormData["options"] );
if ( !task.IsFaulted )
{
foreach ( MultipartFileData file in provider.FileData ) {
Debug.Print( file.Headers.ContentDisposition.FileName ); // file name parameter to formData.Add back on client.
Debug.Print( "Server file path: " + file.LocalFileName ); // posted file automatically streamed to path specified by root var above.
}
}
} );
}
catch ( Exception e )
{
; // TBD
}
return new HttpResponseMessage( HttpStatusCode.Accepted );
}
else
{
return new HttpResponseMessage( HttpStatusCode.BadRequest );
}
}