File Upload using MultipartFormDataStreamProvider in ASP.Net WebAPI

In this short tutorial, we are going to see how to upload multiple files using MultipartFormDataStreamProvider in ASP.Net WebAPI. The concept is based on Multipart/form-data in which we can POST not only multiple file contents but also regular form fields which will be available as NameValueCollection on server side.In this tutorial we also see how to override the default behavior of MultipartFormDataStreamProvider which stores the name in a unique BodyPart_{GUID} format to much more meaningful name. We will also invoke our WebAPI using Fiddler to POST file data. Alongside we develop a sample console application which will POST file data using HttpClient class.

Code is pretty much self-explanatory. Comments are provided at necessary points for better understandability.

ASP.Net Web API (Create a New Project, then create ApiControlller, please the following code in it)

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;

namespace UploadApplication.Controllers
{
    public class UploadController : ApiController
    {
        public async Task<HttpResponseMessage> Post()
        {
            // Check whether the POST operation is MultiPart?
            if (!Request.Content.IsMimeMultipartContent())
            {
                throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
            }

            // Prepare CustomMultipartFormDataStreamProvider in which our multipart form
            // data will be loaded.
            string fileSaveLocation = HttpContext.Current.Server.MapPath("~/App_Data");
            CustomMultipartFormDataStreamProvider provider = new CustomMultipartFormDataStreamProvider(fileSaveLocation);
            List<string> files = new List<string>();

            try
            {
                // Read all contents of multipart message into CustomMultipartFormDataStreamProvider.
                await Request.Content.ReadAsMultipartAsync(provider);

                foreach (MultipartFileData file in provider.FileData)
                {
                    files.Add(Path.GetFileName(file.LocalFileName));
                }

                // Send OK Response along with saved file names to the client.
                return Request.CreateResponse(HttpStatusCode.OK, files);
            }
            catch (System.Exception e)
            {
                return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
            }
        }
    }

    // We implement MultipartFormDataStreamProvider to override the filename of File which
    // will be stored on server, or else the default name will be of the format like Body-
    // Part_{GUID}. In the following implementation we simply get the FileName from 
    // ContentDisposition Header of the Request Body.
    public class CustomMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
    {
        public CustomMultipartFormDataStreamProvider(string path) : base(path) { }

        public override string GetLocalFileName(HttpContentHeaders headers)
        {
            return headers.ContentDisposition.FileName.Replace("\"", string.Empty);
        }
    }
}

 

Testing using Fiddler –

image

 

Response –

image

 

Now lets create a simple Console application which will programmatically POST file contents to our above API –

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var client = new HttpClient())
            using (var content = new MultipartFormDataContent())
            {
                // Make sure to change API address
                client.BaseAddress = new Uri("http://localhost:53798/");

                // Add first file content 
                var fileContent1 = new ByteArrayContent(File.ReadAllBytes(@"c:\Users\aisadmin\Desktop\Me\NF2202533167366.pdf"));
                fileContent1.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
                {
                    FileName = "Sample.pdf"
                };

                // Add Second file content
                var fileContent2 = new ByteArrayContent(File.ReadAllBytes(@"c:\Users\aisadmin\Desktop\Sample.txt"));
                fileContent2.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
                {
                    FileName = "Sample.txt"
                };

                content.Add(fileContent1);
                content.Add(fileContent2);

                // Make a call to Web API
                var result = client.PostAsync("/api/upload", content).Result;

                Console.WriteLine(result.StatusCode);
                Console.ReadLine();
            }
        }
    }
}

 

Output of above code can be examined by navigating to App_Data folder of the Web API –

image

Happy Coding!!!

You may also like...

3 Pingbacks/Trackbacks

  • Pingback: Blog Posts of the Week (14th - 20th July 2013) - The South Asia MVP Blog - Site Home - TechNet Blogs()

  • Behrad

    Hello

    if i want to send a parameter with attachments , how i should do that ?

    let’s say these are user images and i need to send USER ID as well to api

    so api can insert to database for that USER id

    thanks

  • RamiVemula

    @Behrad – Look at this multipart formdata – http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2. There using provider.FormData you can get the keyvaluepairs of your data too.

  • Behrad

    thanks

  • Pingback: Uploading file with post from ASP.NET web api throws error when published to Azure but not localhost - CSS PHP()

  • Pingback: Blog Posts of the Week (14th – 20th July 2013) | India MVP Community()

  • Simon Azzopardi

    Hello,

    I am using exactly your code, but not all attachments are being uploaded correctly. For example docs.. when trying to open them, they become corrupted and will not allow me to open file.

    Any idea?

    HttpContext currentHttpContext = HttpContext.Current;
    var guidFromQueryString = currentHttpContext.Request[ConstantValues.Query_String_GUID_name_For_Attachments];
    string fileSaveLocation = currentHttpContext.Server.MapPath(ConstantValues.Temp_Attachments_Folder_Path + guidFromQueryString);

    System.IO.Directory.CreateDirectory(fileSaveLocation);

    List files = new List();

    var provider = new CustomMultipartFormDataStreamProvider.CustomMultipartFormDataStreamProvider(fileSaveLocation);

    request.Content.ReadAsMultipartAsync(provider);

  • ramiramilu

    Hi Simon,

    I just tried with a DOCX document and it got uploaded successfully with out any error. In fact I was able to open the uploaded document. Please check your code and see if there are any permissions issues while writing the document. Also try to replicate the same code AS-IS and check with a console application (as demonstrated in the tutorial).

  • Simon Azzopardi

    Worked now. I removed the last part where there are the foreach loop. I don’t know why :) Cheers