HttpClient Multipart Form Post in C#

public class CourierMessage
{
    public string Id { get; set; }
    public string Key { get; set; }
    public string From { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
    public DateTimeOffset Processed { get; set; }
    public DateTime Received { get; set; }
    public DateTime Created { get; set; }
    public DateTime Sent { get; set; }
    public HttpPostedFileBase File { get; set; }
}  




while (true)
{
    Console.WriteLine("Hit any key to make request.");
    Console.ReadKey();

    using (var client = new HttpClient())
    {
        using (var multipartFormDataContent = new MultipartFormDataContent())
        {
            var values = new[]
            {
                new KeyValuePair<string, string>("Id", Guid.NewGuid().ToString()),
                new KeyValuePair<string, string>("Key", "awesome"),
                new KeyValuePair<string, string>("From", "[email protected]")
                 //other values
            };

            foreach (var keyValuePair in values)
            {
                multipartFormDataContent.Add(new StringContent(keyValuePair.Value), 
                    String.Format("\"{0}\"", keyValuePair.Key));
            }

            multipartFormDataContent.Add(new ByteArrayContent(File.ReadAllBytes("test.txt")), 
                '"' + "File" + '"', 
                '"' + "test.txt" + '"');

            var requestUri = "http://localhost:5949";
            var result = client.PostAsync(requestUri, multipartFormDataContent).Result;
        }
    }
}  

enter image description here


This is an example of how to post string and file stream with HTTPClient using MultipartFormDataContent. The Content-Disposition and Content-Type need to be specified for each HTTPContent:

Here's my example. Hope it helps:

private static void Upload()
{
    using (var client = new HttpClient())
    {
        client.DefaultRequestHeaders.Add("User-Agent", "CBS Brightcove API Service");

        using (var content = new MultipartFormDataContent())
        {
            var path = @"C:\B2BAssetRoot\files\596086\596086.1.mp4";

            string assetName = Path.GetFileName(path);

            var request = new HTTPBrightCoveRequest()
            {
                Method = "create_video",
                Parameters = new Params()
                {
                    CreateMultipleRenditions = "true",
                    EncodeTo = EncodeTo.Mp4.ToString().ToUpper(),
                    Token = "x8sLalfXacgn-4CzhTBm7uaCxVAPjvKqTf1oXpwLVYYoCkejZUsYtg..",
                    Video = new Video()
                    {
                        Name = assetName,
                        ReferenceId = Guid.NewGuid().ToString(),
                        ShortDescription = assetName
                    }
                }
            };

            //Content-Disposition: form-data; name="json"
            var stringContent = new StringContent(JsonConvert.SerializeObject(request));
            stringContent.Headers.Add("Content-Disposition", "form-data; name=\"json\"");
            content.Add(stringContent, "json");

            FileStream fs = File.OpenRead(path);

            var streamContent = new StreamContent(fs);
            streamContent.Headers.Add("Content-Type", "application/octet-stream");
            streamContent.Headers.Add("Content-Disposition", "form-data; name=\"file\"; filename=\"" + Path.GetFileName(path) + "\"");
            content.Add(streamContent, "file", Path.GetFileName(path));

            //content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");

            Task<HttpResponseMessage> message = client.PostAsync("http://api.brightcove.com/services/post", content);

            var input = message.Result.Content.ReadAsStringAsync();
            Console.WriteLine(input.Result);
            Console.Read();
        }
    }
}

So the problem I'm seeing is that the MultipartFormDataContent request message will always set the content type of the request to "multipart/form-data". Endcoding json and placing that into the request only "looks" like to the model binder as a string.

Your options are:

  • have your mvc action method receive a string and deserialize into your object
  • post each property of your model as a form part
  • create a custom model binder that will handle your request.
  • Breakup the operation into two posts, first sends the json metadata, the other sends the file. The response from the server should send some id or key to correlate the two requests.

Reading through the RFC document and the MSDN documentation you may be able to do this, if you replace MultipartFormDataContent with MultipartContent. But I have not tested this yet.