Uploading Files Asynchronously using ASP.NET Web API

Introduction

Uploading files from the client machine to the web server is a very common operation. Typically ASP.NET developers use HTML file input fields and form submission to accomplish this task. By default this file upload process is synchronous. By using Web API in combination with task-based programming, introduced in .NET 4 you can easily upload files on the server in asynchronous manner. In this article you will learn how to initiate a file upload operation using jQuery and then save the posted files on  the server using asynchronous Web API.

Sample Form to Upload Files

To illustrate how files can be uploaded to the server in asynchronous fashion you will develop a simple form as shown below:

Form to upload files
Form to upload files

The ASP.NET MVC view shown above contains an HTML5 file input field and a button. HTML5 allows you to select multiple files to upload to the server. All you need to do is set the multiple attribute of the file input field. The following markup shows how this can be done:

<form action="/api/fileupload" method="post" enctype="multipart/form-data">
    <span>Select file(s) to upload :</span>
    <input id="file1" type="file" multiple="multiple" />
    <input id="button1" type="submit" value="Upload" />
</form>

Observe the <form> shown above carefully. The action attribute of the <form> tag is set to /api/fileupload. This is the Web API that you will develop in the following sections. The enctype attribute of the form is set to multipart/form-data. This is important for uploading files using the traditional form submission technique. The file input field has its multiple attribute set to multiple indicating that you can select multiple files to upload. The above markup uses an input field of type submit that triggers the file upload operation. In the example discussed in the following sections you will be using jQuery to initiate the file upload operation. Considering this you can also use an input field of type button as shown below.

<input id="button1" type="button" value="Upload" />

If you are using jQuery to initiate the file uploads you need not set action and enctype attributes because they will be taken care of by the jQuery code. The above view still uses them just in case you decide to try the code with the traditional way of uploading files.

Creating Web API for Receiving and Saving the Uploaded Files

To develop this example, begin by creating a new ASP.NET MVC4 Web API project.

Create a new ASP.NET MVC4 Web API project
Create a new ASP.NET MVC4 Web API project

Change the default ApiController name (file name as well as the class name) to FileUploadController. Delete all of the methods from the ApiController except Post(). This is done since the file upload will be done using an HTTP POST operation. Then modify the Post() method is shown below:

public Task<IEnumerable<string>> Post()
{
    if (Request.Content.IsMimeMultipartContent())
    {
        string fullPath = HttpContext.Current.Server.MapPath("~/uploads");
        MyMultipartFormDataStreamProvider streamProvider = new MyMultipartFormDataStreamProvider(fullPath);
        var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith(t =>
            {
                if (t.IsFaulted || t.IsCanceled)
                    throw new HttpResponseException(HttpStatusCode.InternalServerError);
                        
                var fileInfo = streamProvider.FileData.Select(i =>
                {
                    var info = new FileInfo(i.LocalFileName);
                    return "File uploaded as " + info.FullName + " (" + info.Length + ")"; 
                });
                return fileInfo;
                        
            });
        return task;
    }
    else
    {
        throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "Invalid Request!"));
    }
}

As you can see the Post() method uses a task-based pattern. It returns a task with IEnumerable of strings. If you want, you could have also returned IEnumerable of objects by returning custom objects with more detailed information about the file being uploaded.

The Post() method first checks whether the form content being posted is multipart using the IsMimeMultipartContent() method. The IsMimeMultipartContent() method returns true if the content is multipart content. It then determines the destination folder for saving the uploaded files. In this example the folder name - uploads - is used but you can make it more configurable so that you can easily change it later if required.

Then an instance of MyMultipartFormDataStreamProvider class is created. Again, the MyMultipartFormDataStreamProvider class is a custom class that inherits from the MultipartFormDataStreamProvider base class. The MultipartFormDataStreamProvider class essentially provides the basic functionality of saving the uploaded files to a file stream. The MultipartFormDataStreamProvider checks the Content-Disposition header and determines an output stream based on the presence of a filename parameter. If a filename parameter is present then the content is written to a file stream, otherwise it is written to a memory stream. Just to understand how a content disposition header looks, see the following sample header:

 content-disposition: form-data ; name="file1"; filename="employees.xml"
Content-Type: text/plain

Why do you need to create a custom class based on MultipartFormDataStreamProvider? Because the default implementation saves the files with arbitrary file names on the server. If you wish to have the destination file name the same as the source file name from the client machine, you need to do that job on your own. That is what MyMultipartFormDataStreamProvider class does. Here is how:

public class MyMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
{
    public MyMultipartFormDataStreamProvider(string path) : base(path)
    {

    }

    public override string GetLocalFileName(System.Net.Http.Headers.HttpContentHeaders headers)
    {
        string fileName;
        if (!string.IsNullOrWhiteSpace(headers.ContentDisposition.FileName))
        {
            fileName=headers.ContentDisposition.FileName;
        }
        else
        {
            fileName=Guid.NewGuid().ToString() + ".data";
        }
        return fileName.Replace("\"", string.Empty);
    }
}

As you can see the MyMultipartFormDataStreamProvider class inherits from the MultipartFormDataStreamProvider base class and overrides the GetLocalFileName() method. Inside the GetLocalFileName() method you essentially return a file name by picking it from the ContentDisposition header. While instantiating the MyMultipartFormDataStreamProvider object, the full destination path where the file needs to be saved is passed in the constructor.

The Post() method then calls the ReadAsMultipartAsync() method. The ReadAsMultipartAsync() method reads MIME body parts in asynchronous fashion and depending on their content disposition they are saved as a file, as explained earlier.

It would be helpful if after uploading the files on the server you send the file details to the client so that the same can be displayed to the end user. This can be done by returning a string with full server side path of the file and its size in bytes. The FileData property of the MultipartFormDataStreamProvider class provides the collection of multipart files that are being uploaded. So, the code is basically creating a string message for each file being uploaded using the FormData parts.

This completes the Web API. Now let's develop a jQuery client that calls this Web API.

Uploading Files Using jQuery

To consume the Web API you just developed, add an Index view for the HomeController Index() action method. Make sure to add the <form> element as discussed earlier in this article.

In order to upload files via jQuery you need to programmatically construct a FormData object on the client side and then pass it to the server. The FormData object is passed to the server using jQuery $.ajax(). The following jQuery code shows how this is done.

$(document).ready(function () {
    $("#button1").click(OnUpload);
});

function OnUpload(evt) {
    var files = $("#file1").get(0).files;
    if (files.length > 0) {
        if (window.FormData !== undefined) {
            var data = new FormData();
            for (i = 0; i < files.length; i++) {
                data.append("file" + i, files[i]);
            }
            $.ajax({
                type: "POST",
                url: "/api/fileupload",
                contentType: false,
                processData: false,
                data: data,
                success: function (results) {
                    for (i = 0; i < results.length; i++) {
                        alert(results[i]);
                    }
                }
            });
        } else {
            alert("This browser doesn't support HTML5 multiple file uploads!");
        }
    }
}

The ready() function wires the click event of the Upload button with an event handler function - OnUpload. The OnUpload() function retrieves the collection of files selected by the user for uploading. Recollect that the file input field has its multiple attribute set. The files selected by the use are available via files collection of the file input field. Notice that to get hold of this collection you need to use get(0) of jQuery that returns the raw DOM element (file field in this case).

Then a FormData object is programmatically constructed and populated with selected files. To send the files to the server you use $.ajax() of jQuery. The type of the Ajax request is POST (this verb must match the Web API method). The Url option specifies the name of the Web API end-point (/api/fileupload in this case). The data option is set to the FormData object you created earlier. The contentType and processData options are set to false because you wish to upload a multipart content in raw fashion rather than its URL encoded version (the default way). The success function receives a collection of string objects you return from the Post() method. The for loop iterates through the collection and displays the string message returned from the Web API to the end user.

That's it! You can now run the Index view and test how the files are uploaded to the server. 

Summary

Uploading files from the client to the web server is a common requirement. Using ASP.NET Web API combined with task-based programming, you can easily develop a solution that uploads files asynchronously. In this article you learned how the upload operation can be initiated using jQuery and how files can be saved on the server using a custom MultipartFormDataStreamProvider. Though this article uses the file input field to select files you could have also use HTML5 drag-n-drop to select files and then you could have handled drop event instead of the click event.



Related Articles

Downloads

Comments

  • Multiple client

    Posted by 4nh7i3m on 03/01/2014 09:47pm

    I like your solution using MimeMultipartContent. I also found an article that uses same technique and provides also example code for Android client

    Reply
  • Call Back Method

    Posted by Akhil on 12/31/2013 02:06am

    Hi, Thanks for such a nice tutorial. Just want to know how can we implement call back method after file is uploaded successfully. Regards, Akhil Mittal.

    Reply
  • How to store file into sqlserver instead of file system

    Posted by Jun on 07/04/2013 08:04pm

    How to store file into sqlserver directly without storing it on the file system.

    Reply
  • The reasons most people are dead wrong over sneakers and reasons why you really should check this article.

    Posted by BobHotgloff on 05/23/2013 07:22am

    Concepts behind shoes that you can make use of commencing today. [url=http://www.shoesjp.biz/new-balance【ニューバランス】-c-670.html]ニューバランス キッズ[/url] For what reason anything and everything that you have read about shoes is in fact wrong and exactly what you need understand. [url=http://www.shoesjp.biz/nike【ナイキ】-c-634.html]ナイキスニーカー[/url] Efficient article aids you with most of the inner workings of the shoes together with those things that you want to do this afternoon. [url=http://www.kutujp.biz/]アシックス[/url] Upcoming sneakers Publication Demonstrates How To Dominate The shoes Arena [url=http://www.kutujp.biz/アディダス-adidas-c-4.html]adidas アディダス[/url] The particular reason why all things that you have discovered about sneakers is undoubtedly completely wrong and exactly what you need understand. [url=http://www.kutujp.biz/アシックス-asics-c-3.html]アシックス すくすく[/url] The ideal approach for the shoes that you may discover now. [url=http://www.kutujp.biz/ナイキ-nike-c-13.html]ナイキスニーカー[/url] Cutting edge guide explains the know-how for shoes and additionally reasons why you must take action immediately. [url=http://www.kutujapan.org/]アシックス[/url] All new shoes Book Presents Very Best Way To Dominate The shoes Arena [url=http://www.kutujapan.org/adidas-アディダス-c-74.html]アディダス[/url] Innovative shoes Ebook Exposes Techniques To Rule The sneakers World [url=http://www.kutujapan.org/new-balance-ニューバランス-c-13.html]ニューバランス 574[/url] What pro's aren't stating on the subject of shoes and the way that this have an effect on you actually. [url=http://www.kutujapan.org/nike-ナイキ-c-78.html]ナイキ[/url] The main reason why most people are dead wrong in relation to sneakers and as a consequence why you will have to look at this review.

    Reply
  • Unbiased review reveals A few great new things surrounding nike shoes that none is covering.

    Posted by moisseenfogma on 05/20/2013 07:48am

    P [url=http://www.nikekutuja.biz/]ナイキスニーカー[/url] crAgz HogYua ZjlLqu Bmo [url=http://www.nikekutuja.biz/air-jordan空気ヨルダン-c-1.html]nike air jordan[/url] QtcGzkGhy Lg [url=http://www.nikekutuja.biz/air-maxエアマックス-c-2.html]air max 95[/url] yXfsIaxHwb CbxZaaBhr [url=http://www.nikekutuja.biz/nike-air-force-1ナイキエアフォース-c-4.html]air force[/url] Hjv SnhXbv Mf [url=http://www.nikekutuja.com/]nike running[/url] mNjp KudWna YmeYga KcmC [url=http://www.nikekutuja.com/air-jordan空気ヨルダン-c-1.html]air jordan[/url] feBiiAlx RplUixUphQ [url=http://www.nikekutuja.com/nike-dunkナイキダンク-c-4.html]nike store[/url] qr VswXcrIh [url=http://www.nikekutuja.com/air-maxエアマックス-c-2.html]ナイキ エアマoックス[/url] aHmf JnhGqm Lw [url=http://www.nikekutujp.com/]nike running[/url] pRfu IulLjt FviEzc QyzG [url=http://www.nikekutujp.com/nike-dunkナイキダンク-c-4.html]nike store[/url] izYtcDnl X [url=http://www.nikekutujp.com/nike-air-force-1ナイキエアフォース-c-3.html]ナイキ エアフォース1[/url] rxGvkRqnOrx [url=http://www.nikekutujp.com/air-jordan空気ヨルダン-c-1.html]nike air jordan[/url] KwkAgdJaoXow IeoGwf

    Reply
  • Its always necessary keep your teeth clean

    Posted by tamrinnalon on 04/28/2013 02:18am

    A tooth (plural teeth) is a mignonne, calcified, whitish build found in the jaws (or mouths) of innumerable vertebrates and worn to break down food. Some animals, strikingly carnivores, also use teeth for the purpose hunting or in place of defensive purposes. The roots of teeth are covered by gums. Teeth are not made of bone, but degree of multiple tissues of varying density and hardness. The community make-up of teeth is alike resemble across the vertebrates, although there is considerable converting in their form and position. The teeth of mammals get serious roots, and this design is also found in some fish, and in crocodilians. In most teleost fish, however, the teeth are attached to the outer surface of the bone, while in lizards they are fixed devoted to to the inner come up of the jaw by one side. In cartilaginous fish, such as sharks, the teeth are attached by cold ligaments to the hoops of cartilage that accumulate the jaw.

    Reply
  • returned task

    Posted by T on 04/10/2013 11:56pm

    thanks for the article, it works great. I have a little problem: I get a success alert, but a second after that - an empty page reloads with the "File uploaded as..." message. Why is this happening?

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Instead of only managing projects organizations do need to manage value! "Doing the right things" and "doing things right" are the essential ingredients for successful software and systems delivery. Unfortunately, with distributed delivery spanning multiple disciplines, geographies and time zones, many organizations struggle with teams working in silos, broken lines of communication, lack of collaboration, inadequate traceability, and poor project visibility. This often results in organizations "doing the …

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds