How to parse MultipartFormDataContent

8.1k views Asked by At

I am writing a Web API service where I want to accept a file (image) and a serialized object (JSON) that contains key information about the image. Not having issues with the image part but when I add string content containing the deserialized object I am having issues in trying to determine which is which and act accordingly.

The client code looks like:

HttpClient client = new HttpClient();

MultipartFormDataContent content = new MultipartFormDataContent();
content.Add(new StreamContent(File.Open("c:\\MyImages\\Image00.jpg", FileMode.Open)), "image_file", "Image00.jpg");

ImageKeys ik = new ImageKeys { ImageId = "12345", Timestamp = DateTime.Now.ToString() };
JavaScriptSerializer js = new JavaScriptSerializer();
if (ik != null)
{
    content.Add(new StringContent(js.Serialize(ik), Encoding.UTF8, "application/json"), "image_keys");
}

string uri = "http://localhost/MyAPI/api/MyQuery/TransferFile";
var request = new HttpRequestMessage()
{
    RequestUri = new Uri(uri),
    Method = HttpMethod.Post
};

request.Content = content;

string responseStr = "";
try
{
    HttpResponseMessage result = client.SendAsync(request).Result;
    string resultContent = string.Format("{0}:{1}", result.StatusCode, result.ReasonPhrase);

    //
    // Handle the response
    //
    responseStr = resultContent;
}
catch (Exception ex)
{
    responseStr = ex.Message;
}

listBox1.Items.Add(responseStr);

So I include the image file first followed by a serialized object as StringContent. On the server side I am using the following code to parse the message.

HttpRequestMessage request = this.Request;
HttpResponseMessage ret = new HttpResponseMessage();

//
// Verify that this is an HTML Form file upload request
//
if (!request.Content.IsMimeMultipartContent())
{
    throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}

string root = "c:\\tmp\\uploads";
if (!Directory.Exists(root))
{
    Directory.CreateDirectory(root);
}

//
// Create a stream provider for setting up output streams that saves the output under c:\tmp\uploads
// If you want full control over how the stream is saved then derive from MultipartFormDataStreamProvider
// and override what you need.
//
MultipartFormDataStreamProvider streamProvider = new MultipartFormDataStreamProvider(root);

try
{
    await request.Content.ReadAsMultipartAsync(streamProvider);
    foreach (var file in streamProvider.Contents)
    {
        if (file.Headers.ContentDisposition.Name == "image_file")
        {
            FileInfo finfo = new FileInfo(streamProvider.FileData.First().LocalFileName);

            string destFile = Path.Combine(root, streamProvider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", ""));
            //
            // File.Move cannot deal with duplicate files
            // Ensure that the target does not exist. 
            //
            if (File.Exists(destFile))
            {
                File.Delete(destFile);
            }

            File.Move(finfo.FullName, destFile);
        }
        else if (file.Headers.ContentDisposition.Name == "image_keys")
        {
            // deserialize key class
            string str = file.ReadAsStringAsync().Result;

            JavaScriptSerializer js = new JavaScriptSerializer();
            ImageKeys ik = js.Deserialize<ImageKeys>(str);
        }
    }

    ret.StatusCode = HttpStatusCode.OK;
    ret.Content = new StringContent("File uploaded.");
}
catch (Exception ex)
{
    ret.StatusCode = HttpStatusCode.UnsupportedMediaType;
    ret.Content = new StringContent("File upload failed.");
}

return ret;

The foreach loop tries to process each item in the multipart content as a file but I want to treat the various content types separately but it is not clear to me how they are delineated. Thanks

1

There are 1 answers

0
Eugene On

You can cast Content to MultipartFormDataContent and iterate thru it. Based on content type you can read it as a file or string. Example for string content type:

var dataContents = request.Content as MultipartFormDataContent;

foreach (var dataContent in dataContents)
{
    var name = dataContent.Headers.ContentDisposition.Name;
    var value = dataContent.ReadAsStringAsync().Result;
    ...
}