Web API File Uploading (Desktop and Web client)

File uploading is one of the most common tasks all developers have to deal with, at some point of their application development cycle. For example, Web applications (such as social networks) usually require users to upload a profile picture during registration or in the case of desktop ones, users may required to upload private documents.
This post will show you how to upload multipart/form-data files using Web API with both Web and Desktop clients. More particular here’s what we gonna see:

  • Web API File Upload Controller: Create an action for Uploading multipart-form data
  • Web client: Create an AngularJS web client to upload multiple files at once using the Angular File Upload module
  • Desktop client: Create an Windows Form client to upload multiple files using HttpClient

Let’s start:

The Controller

Create a solution named WebApiFileUpload and add a new empty Web Application named WebApiFileUpload.API selecting both the Web API and MVC checkboxes. Add a Web API Controller named FileUploadController. Before showing controller’s code let’s see what this controller requires:

  • A FileUpload result: Custom result to return to clients
  • A custom MultipartFormDataStreamProvider: The provider which will actually capture and save the uploaded file
  • Allow only Mime-Multipart content to be uploaded:For this our best option is to create a Filter and apply it to the POST method

Create a Infrastructure folder inside the API project and add the following three classes that implement the above required behaviors.

public class FileUploadResult
    {
        public string LocalFilePath { get; set; }
        public string FileName { get; set; }
        public long FileLength { get; set; }
    }
public class UploadMultipartFormProvider : MultipartFormDataStreamProvider
    {
        public UploadMultipartFormProvider(string rootPath) : base(rootPath) { }

        public override string GetLocalFileName(HttpContentHeaders headers)
        {
            if (headers != null &&
                headers.ContentDisposition != null)
            {
                return headers
                    .ContentDisposition
                    .FileName.TrimEnd('"').TrimStart('"');
            }

            return base.GetLocalFileName(headers);
        }
    }
public class MimeMultipart : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (!actionContext.Request.Content.IsMimeMultipartContent())
            {
                throw new HttpResponseException(
                    new HttpResponseMessage(
                        HttpStatusCode.UnsupportedMediaType)
                );
            }
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {

        }
    }

And now the controller:

public class FileUploadController : ApiController
    {
        [MimeMultipart]
        public async Task<FileUploadResult> Post()
        {
            var uploadPath = HttpContext.Current.Server.MapPath("~/Uploads");

            var multipartFormDataStreamProvider = new UploadMultipartFormProvider(uploadPath);

            // Read the MIME multipart asynchronously 
            await Request.Content.ReadAsMultipartAsync(multipartFormDataStreamProvider);

            string _localFileName = multipartFormDataStreamProvider
                .FileData.Select(multiPartData => multiPartData.LocalFileName).FirstOrDefault();

            // Create response
            return new FileUploadResult
            {
                LocalFilePath = _localFileName,

                FileName = Path.GetFileName(_localFileName),

                FileLength = new FileInfo(_localFileName).Length
            };
        }
    }

Make sure you create a folder named Uploads inside the project cause this is where the uploaded files will be saved. The uploaded result will contain the file uploaded local path in the server, file’s name and length in bytes. You can add any other properties you want.

AngularJS Web client

It’s time to create a web client using AngularJS that will be able to upload multiple files using the controller we have just created. As we have mentioned we are going to use the AngularJS File Upload module which can be found here. Create a Scripts folder inside the API project and you need to add the following javascript files:

  1. angular.js
  2. angular-file-upload.js
  3. angular-route.js
  4. jquery-1.8.2.js (you can use any other version if you want)

Also add the bootstrap.css inside a Styles folder. Add a HomeController MVC Controller and add an Index() action if not exists. Create the respective View for this action and paste the following code:

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
    <link href="~/Styles/bootstrap.css" rel="stylesheet" />
</head>
<body ng-app="angularUploadApp">
    <div class="container">
        <div class="jumbotron">
            <p>Web API File uploading using AngularJS client. Read more at <a href="https://chsakell.com/2015/06/07/web-api-file-uploading-desktop-and-web-client" target="_blank">chsakell.com/</a>.</p>
        </div>
        <div ng-include="'app/templates/fileUpload.html'"></div>

    </div>
    <script src="Scripts/jquery-1.8.2.js"></script>
    <script src="Scripts/angular.js"></script>
    <script src="Scripts/angular-file-upload.js"></script>
    <script src="Scripts/angular-route.js"></script>
    <script src="app/app.js"></script>
    <script src="app/controllers/fileUploadCtrl.js"></script>

</body>
</html>

We haven’t created either the angularJS fileUpload.html or the app.js and fileUploadCtrl.js yet, so let’s add them. You have to create the following folders first:

  1. app
  2. app/controllers
  3. app/templates
'use strict';

angular.module('angularUploadApp', [
    'ngRoute',
    'angularFileUpload'
])

.config(function ($routeProvider) {
    $routeProvider
    .when('/', {
        templateUrl: 'app/templates/fileUpload.html',
        controller: 'fileUploadCtrl'
    })
    .otherwise({
        redirectTo: '/'
    });
});
'use strict';

angular.module('angularUploadApp')
    .controller('fileUploadCtrl', function ($scope, $http, $timeout, $upload) {
        $scope.upload = [];
        $scope.UploadedFiles = [];

        $scope.startUploading = function ($files) {
            //$files: an array of files selected
            for (var i = 0; i < $files.length; i++) {
                var $file = $files[i];
                (function (index) {
                    $scope.upload[index] = $upload.upload({
                        url: "./api/fileupload", // webapi url
                        method: "POST",
                        file: $file
                    }).progress(function (evt) {
                    }).success(function (data, status, headers, config) {
                        // file is uploaded successfully
                        $scope.UploadedFiles.push({ FileName: data.FileName, FilePath: data.LocalFilePath, FileLength : data.FileLength });
                    }).error(function (data, status, headers, config) {
                        console.log(data);
                    });
                })(i);
            }
        }
    });
<div class="row" ng-controller="fileUploadCtrl">
    <div class="col-xs-3">
        <div>
            <input type="file" ng-file-select="startUploading($files)" multiple>
        </div>
    </div>
    <div class="col-xs-9">
        <div class="panel panel-default" ng-repeat="uploadedFile in UploadedFiles track by $index">
            <div class="panel-heading">
                <strong>{{uploadedFile.FileName}}</strong>
            </div>
            <div class="panel-body">
                <div class=" media">
                    <a class="pull-left" href="#">
                        <img class="media-object" width="100" ng-src="../uploads/{{uploadedFile.FileName}}" />
                    </a>
                    <div class="media-body">
                        <div class="lead" style="font-size:14px;color: crimson;width:500px;word-wrap:break-word">{{uploadedFile.FilePath}}</div>
                    </div>
                </div>
            </div>
            <div class="panel-footer">
                File total bytes: <span style="color:black">{{uploadedFile.FileLength}}</span>
            </div>
        </div>
    </div>
</div>

Build and fire your application. Select multiple files to upload and ensure that all worked as expected.
webapi-file-upload-01

Desktop Client

For the Windows Form client you need to add a new Windows Form project named WebApiFileUpload.DesktopClient and add reference to the WebApiFileUpload.API project (just to access the FileUploadResult class). You also need to install the Microsoft.AspNet.WebApi.Client through the Nuget Packages. I have created a simple Form that allows the user to select multiple files and upload them through the API Controller we created. Let’s see the main method that does that.

try
                {
                    HttpClient httpClient = new HttpClient();
                    // Read the files 
                    foreach (String file in openFileDialog1.FileNames)
                    {
                        var fileStream = File.Open(file, FileMode.Open);
                        var fileInfo = new FileInfo(file);
                        FileUploadResult uploadResult = null;
                        bool _fileUploaded = false;

                        var content = new MultipartFormDataContent();
                        content.Add(new StreamContent(fileStream), "\"file\"", string.Format("\"{0}\"", fileInfo.Name)
                        );

                        Task taskUpload = httpClient.PostAsync(uploadServiceBaseAddress, content).ContinueWith(task =>
                        {
                            if (task.Status == TaskStatus.RanToCompletion)
                            {
                                var response = task.Result;

                                if (response.IsSuccessStatusCode)
                                {
                                    uploadResult = response.Content.ReadAsAsync<FileUploadResult>().Result;
                                    if (uploadResult != null)
                                        _fileUploaded = true;

                                    // Read other header values if you want..
                                    foreach (var header in response.Content.Headers)
                                    {
                                        Debug.WriteLine("{0}: {1}", header.Key, string.Join(",", header.Value));
                                    }
                                }
                                else
                                {
                                    Debug.WriteLine("Status Code: {0} - {1}", response.StatusCode, response.ReasonPhrase);
                                    Debug.WriteLine("Response Body: {0}", response.Content.ReadAsStringAsync().Result);
                                }
                            }

                            fileStream.Dispose();
                        });

                        taskUpload.Wait();
                        if (_fileUploaded)
                            AddMessage(uploadResult.FileName + " with length " + uploadResult.FileLength
                                            + " has been uploaded at " + uploadResult.LocalFilePath);
                    }

                    httpClient.Dispose();
                }
                catch (Exception ex)
                {
                    AddMessage(ex.Message);
                }

Of course you need to run the API Project first before trying to upload files using the Desktop client.
webapi-file-upload-02
That’s it, we are done! We saw how to upload multipart-form data files using Web API from both Web and Desktop clients. I hope you have enjoyed the post. You can download the project we built from here.

In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.

Facebook Twitter
.NET Web Application Development by Chris S.
facebook twitter-small
Advertisements


Categories: Angular, ASP.NET

Tags: ,

8 replies

  1. thanks for share. it’s good for me.

  2. Great article. Just what I had been searching for.
    If you could find the time it would be super if you could do an article about “Multiple StreamContent” ….

  3. I’m trying to upload with [RequireHttps] in the header of the procedure. But this is not enough to get it working.
    Any ideas?

  4. I’m trying to upload to a SSL site (https).
    How should the code be changed. I can upload a file using the REST console of google. So the server site is working.

Trackbacks

  1. Web API File Uploading (Desktop and Web client) | Dinesh Ram Kali.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Chara Plessa

The purpose of this blog is to broaden my education, promote experimentation and enhance my professional development. Albert Einstein once said that “If you can’t explain it simply, you don’t understand it well enough” and I strongly believe him!

chsakell's Blog

Anything around ASP.NET MVC,WEB API, WCF, Entity Framework & AngularJS

Kumikoro

A Front End Developer's Blog

Muhammad Hassan

Full Stack developer with expertise in ASP.NET | MVC | WebAPI | Advanced Javascript | AngularJS | Angular2 | C# | ES6 | SQL | TypeScript | HTML5 | NodeJS, NUCES-FAST CS grad, MS candidate @LUMS, EX-Adjunct Faculty @NUCES-FAST, seasonal blogger & open-source contributor. Seattle, WA

Software Engineering

Web development

IEvangelist

.NET, ASP.NET, C#, MVC, TypeScript, AngularJS

leastprivilege.com

Dominick Baier on Identity & Access Control

Happy DotNetting

In Love with Technology

Knoldus

Knols of experience to your advantage

knowshnet

Search - Read - Request - Share

Rahul's space

Learn, Share and Grow with me !

Dhananjay Kumar

Developer Evangelist @Infragistics | MVP @Microsoft |

Journey to SQL Authority with Pinal Dave

SQL, SQL Server, MySQL, Big Data and NoSQL

Conficient Blog

Random bits of tech from @conficient

Code! Code! Code!

SOLID & KISS

Code Wala

Designing and coding

Microsoft Mentalist

A way to start with Microsoft Technologies

Tony Sneed's Blog

A glimpse into the lives of Tony & Zuzana Sneed

Sriramjithendra Nidumolu

Personal Notes of Sriramjithendra

%d bloggers like this: