Different ways of uploading files using Http based APIs- Part 3

In the last two parts of this series (part 1 and part 2), I showed 2 ways in which we can upload files using the HTTP protocol. In this part, I’ll discuss one more way of doing this i.e. using multipart/related content type for posting files along with related metadata.

Multipart/related specification states that this content type has to be used when you have compound document i.e. where a document has various parts of different types (i.e. different content types) and you can only make sense of it by taking it all together not individually.

This also applies to the scenario where you want to send a file along with its metadata (i.e. information about the file itself). Its good to know that both Google and Microsoft use this format for its file upload APIs.

Below is how the example HTTP request looks like.

POST https://localhost:44390/api/Upload/MultipartRelatedUpload HTTP/1.1
Host: localhost:44390
Content-Type: multipart/Related; boundary="boundary-1"
Content-Length: 20702464

--boundary-1
Content-Type: application/json; charset=utf-8

{"CustomerId":"123123","CustomerName":"ABC Ltd."}
--boundary-1
Content-Type: application/pdf

[PDF CONTENT]
--boundary-1--

Above request has two parts separated by a boundary. The boundary is specified in the header. In fact, this part remains the same for any multipart request.

In the first part, we have JSON content and in the second part, we have the content of the file (in this case application/pdf). Also, you are not restricted to only two parts. In fact, you can have n number of parts all with a different type of content.

Let’s look at a code example.

Example

Here’s how the API looks like.

[HttpPost]

[Route("MultipartRelatedUpload")]

[DisableFormValueModelBinding]

public IActionResult MultipartRelatedUpload()

{

var boundary = MultipartRequestHelper.GetBoundary(MediaTypeHeaderValue.Parse(Request.ContentType), 73);

var reader = new MultipartReader(boundary, HttpContext.Request.Body);

//Read metadata from the first section

var section = reader.ReadNextSectionAsync().Result;

using var firstSectionReader = new StreamReader(section.Body);

var firstSectionContent = firstSectionReader.ReadToEndAsync().Result;

//Read file from the second section

section = reader.ReadNextSectionAsync().Result;

var fileExtension = MimeTypeMap.GetExtension(section.ContentType);

using (var fileStream = System.IO.File.Create(GetFilePath("MultipartRelatedUpload", "None", fileExtension, fileUploadPath)))

{

section.Body.CopyToAsync(fileStream).Wait();

}

return Ok();

}

and below is the client code.

static void PostMultipartFormData()

{

using var client = new HttpClient();

var values = new[]

{

new KeyValuePair<string,string>("CustomerId","123123"),

new KeyValuePair<string,string>("CustomerName","ABC Ltd."),

};

using var content = new MultipartFormDataContent();

foreach(var val in values)

{

content.Add(new StringContent(val.Value), val.Key);

}

var fileContent = new StreamContent(new FileStream(uploadFilePath, FileMode.Open));

fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(fileContentType);

fileContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data")

{

Name = "File",

FileName = "Text.txt"

};

content.Add(fileContent);

var requestUri = "/api/Upload/FormDataUpload";

client.BaseAddress = new Uri(apiBaseAddress);

var result = client.PostAsync(requestUri, content).Result;

Console.WriteLine($"Response : {result.StatusCode}");

}

You can refer the full code at this location.

In the API code, we are basically using MultipartReader class and reading each of the parts of the request as a section and doing appropriate processing over it. For example, we can take the JSON content and use it as metadata whereas the binary content in the pdf section can be saved as a file.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.