How to upload multipart/form-data to ASP.NET Core Web API?


ASP.Net Core File Upload multipart/form-data


Key scenarios


I am a user, having a class of objects with few properties and a file (pdf or image) whatever, to be sent to the server successfully - how do I get it done using Web API?

This article describes the step-by-step simple way of uploading class objects as JSON data and a file (pdf) to a server HTTP Post multipart/form-data.

ASP.NET Core supports uploading one or more files using buffered model binding for smaller files and unbuffered streaming for larger files. 



What Software Tools used?

ASP.NET Core 2.0, Visual Studio 2017, Visual Studio 2019, Web API 2, and C# programming language console application.

Server - .NET Core Web API 2 application which stores the uploaded file

Client - C# Console application consumes server Web API Endpoint.

 

How a file pdf or image sent to a server? 

Unlike the older version, .NET Core introduces an IFormFile Interface. It 'represents a file sent with the HttpRequest' and used namespacesMicrosoft.AspNetCore.Http  IFormFile helps to handle uploaded files with ease.

Using IFormFile in ASP.Net Core uploading multipart/form-data in HTTP Post is much easier.

So now let's see the real code practice of the client and the server.

It is better we complete server-side code first and then define client-side console code. 


1. How the server stores the uploaded file? 

Create a Web API 2 project in Visual Studio 2017 or 2019, if you are new to Web API then click here to know more about how to create a new Web API Project?

1. Create a folder under wwwroot name ex: "upload" in the project solution 

Upload Folder for multipart/form-data post

2. Create a controller example "TestAttachmentController" under Controllers folder
    
 
Create a controller


3. Create a model class with properties that server process from client request.

Note! This model class has special attributes class defined to validate. CustomMaxFileSizeAttribute(Appsettings.FILE_SIZE) defines max size allowed is (5 * 1024 * 1024) 5mb

 Notice 

For now you can ignore or comment out the custom attribute class implemented in the class attribute.


DemoData

public class DemoData
{
         [Required(ErrorMessage = "Please input api key")]
         [StringLength(500, ErrorMessage = "{0} cannot be greater than {1} characters.")]
         [JsonProperty("apiKey")] 
         public string ApiKey { get; set; }

         [Required(ErrorMessage = "Please input attachment type")]
         [StringLength(4, ErrorMessage = "{0} cannot be greater than {1} characters.")]
         [JsonProperty("attachmentType")] 
         public string AttachmentType { get; set; }

         [StringLength(10, ErrorMessage = "{0} cannot be greater than {1} characters.")] 
         [JsonProperty("Sano")]
         public string Sano { get; set; }

         [Required(ErrorMessage = "Please upload pdf file")]
         [JsonProperty("file")]
         [CustomMaxFileSize(AppSettings.FILE_SIZE)] //(5 * 1024 * 1024) 5mb
         [IFormFileValidation(AppSettings.PDF_EXT)] // .pdf
         public IFormFile File { get; set; }
 }


 
Now let's look at controller class. 

Using IFormFile in ASP.Net Core uploading files to server wwwroot folder, we make use of an interface IHostingEnvironment  to access wwwroot folder.

Why to use IHostingEnvironment interface?

Provides information about the web hosting enviornment an application is running in

What is the namespace IHostingEnvironment  Interface uses?

The namespace used is  Microsoft.AspNetCore.Hosting 

The complete code of a Controller class, IHostingEnviornment interface used in a constructor instance. 

What does the controller action method "DemoFormDataUpload"?

It uses [FromFrom] data binding from user requests, validates and then uploads to wwwroot upload folder. 

Okay, When upload success what data server returns to the client application?
Action method returns a class of object properties Status, FileName, and FileSize.  

TestARAttachmentController.cs

using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Hosting;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; 
using System.IO;
using System.Net.Http;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json; 
using System.Net.Http.Headers; 

namespace Api.Controllers
{
   public class TestARAttachmentController : ControllerBase
    {
      public static IHostingEnvironment _environment;
	  
      public TestARAttachmentController(IHostingEnvironment environment)
      {
         _environment = environment;
      }      

      public class DemoData
      {
         [Required(ErrorMessage = "Please input api key")]
         [StringLength(500, ErrorMessage = "{0} cannot be greater than {1} characters.")]
         [JsonProperty("apiKey")] 
         public string ApiKey { get; set; }

         [Required(ErrorMessage = "Please input attachment type")]
         [StringLength(4, ErrorMessage = "{0} cannot be greater than {1} characters.")]
         [JsonProperty("attachmentType")] 
         public string AttachmentType { get; set; }

         [StringLength(10, ErrorMessage = "{0} cannot be greater than {1} characters.")] 
         [JsonProperty("sano")]
         public string Sano { get; set; }

         [Required(ErrorMessage = "Please upload pdf file")]
         [JsonProperty("file")]
         [CustomMaxFileSize(AppSettings.FILE_SIZE)]
         [IFormFileValidation(AppSettings.PDF_EXT)] 
         public IFormFile File { get; set; }
      }

      [HttpPost]
      [ValidateAntiForgeryToken]
      public async Task<IActionResult> DemoFormDataUpload([FromForm] DemoData formData)
      {
         // Check if the request contains multipart/form-data.
         if (formData.File == null)
         {
            return new UnsupportedMediaTypeResult();
         }

         if (formData.File.Length > 0)
         {
            IFormFile formFile = formData.File;

            var folderPath = Path.Combine(_environment.WebRootPath, "upload");
            //var filePath = Path.Combine(folderPath, $"{Path.GetRandomFileName() + Path.GetExtension(formFile.FileName).ToLowerInvariant()}");
            var filePath = Path.Combine(folderPath, formFile.FileName);
            
            if (!Directory.Exists(folderPath))
            {
               Directory.CreateDirectory(folderPath);
            }
            using (var fileStream = new FileStream(filePath, FileMode.Create))
            {
               await formFile.CopyToAsync(fileStream);
               fileStream.Flush();
               return Ok(new {  status = "Upload Success", length = formFile.Length, name = formFile.FileName });
            }
         }
         else
         {
            return NotFound("Failed to upload");
         }
      } 
   }
}


How do I download the uploaded file from server?

Use HttpGet action method pass param as filename and the return types are FileStreamResult  and PhysicalFileResult 

In the below example has three ways to download file, if you notice _environment instance from above controller IHostingEnviornment  

Download From Server

         [HttpGet]
      public PhysicalFileResult DownloadFile(string fileName)
      {
         return new PhysicalFileResult(Path.Combine(_environment.WebRootPath,"upload",fileName),"application/pdf");
      }
       
      [HttpGet]
      public FileStreamResult DownloadFile1(string fileName)
      {
         var filePath = Path.Combine(_environment.WebRootPath, "upload", fileName);
         var fileStream = new FileStream(filePath,FileMode.Open, FileAccess.Read);
         return File(fileStream, "application/pdf", fileName);
      }
      
      [HttpGet]
      public FileStreamResult DownloadFileStream(string fileName)
      {
         var filePath = Path.Combine(_environment.WebRootPath, "upload", fileName);
         var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);

         return new FileStreamResult(fileStream,"application/pdf")
         {
            FileDownloadName = fileName
         };
      }


Until here we have completed server side code sending multipart/form-data to ASP.NET Core Web API endpoint.

Now let's move to client side httpclient post multipart/form-data code, how to consume server code in a C# console application.

 

2. How the client uploads the file? 

Create a C# console application. If you're new to this click here how to create C# Cosole Application? 

If you remeber server controller action method returns a class of object properties status, file name, and file size upon upload success.

In the client application create a model class with three properties

UploadFileResult.cs

  public class UploadFileResult
   {
      public string Status { get; set; }
      public string FileName { get; set; }
      public long FileSize { get; set; }
   }


Create a method "DemoUpload" that consumes our server Web API Endpoint "DemoFormDataUpload".

This method will use httpclient post MultipartFormDataContent (multipart/form-data) class properties and pdf file to server.

Note  When you run server web api copy the url correctly, here I used my localhost web api.


What is done in DemoUpload method here?

1.  Set your upload file path to FileInfo class
2.  Construct payload class properties with values
3.  Add class properties values to MultipartFormDataContent object
4.  Get the FileStreamContent of the file and add to MultipartFormDataContent object
5.  Send it through HttpClient PostAsync

Console application method

static void DemoUpload()
    {
         try
         {
            var fileInfo = new FileInfo(@"C:\Users\Demo\Downloads\yourfile.pdf"); 

            var payloadData = new 
            {             
               ApiKey = "ABC-2id-34lkwk-dkad",
               AttachmentType = "UHVE",
               Sano = "uweor23" 
            };

            var form = new MultipartFormDataContent();

            //Add data model to multiForm Content     
			 
			 content.Add(new StringContent(payloadData.ApiKey), "apiKey");
			 content.Add(new StringContent(payloadData.AttachmentType), "attachmentType");
			 content.Add(new StringContent(payloadData.Sano), "Sano");

            //Add file StreamContent to multiForm Content 
            var fileContent = new StreamContent(new FileStream(fileInfo.FullName, FileMode.Open));
            fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
            fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
            {
               Name = "File",
               FileName = fileInfo.Name,
               FileNameStar = fileInfo.Name
            };
            form.Add(fileContent);

            var client = new HttpClient();
            client.BaseAddress = new Uri("https://localhost:17104");
            var result = client.PostAsync("/TestARAttachment/DemoFormDataUpload", form).Result;

            var fileResult = result.Content.ReadAsAsync<UploadFileResult>().Result;

            Console.WriteLine("***************");
            Console.WriteLine("Status: " + result.StatusCode);
            Console.WriteLine("File name: " + fileResult.Name);
            Console.WriteLine("File size: " + fileResult.Length);
            Console.WriteLine("***************");
         }
         catch (Exception e)
         {
            Console.WriteLine(e.Message);
         }
     }


Main Method call

static void Main(string[] args)
{
	 DemoUpload();
}


So far okay, how do I ensure client and server application works as expected?

1. First run your server Web ApI Code  
2. Set the breakpoint to "DemoFormDataUpload"  action method
3. Now, run your Console application and set the breakpoint to "DemoUpload" method 
4. When httpclient request the endpoint, should be able to hit the breakpoint in server side as well.
5. Finally server code uploads the pdf file to a wwwroot  > Upload folder


Summary 

This article covered a step-by-step code example of HttpPost httpclient multipart/form-data to a server.

How to send or upload multipart/form-data to ASP.NET Core Web API, and how to download uploaded file is easy to do.  

We created ASP.NET Core Web ApI 2 as a server side endpoint code and C# Console application to consume web API endpoint using HttpClient request.