It's Spring and the weather's a bit better and things are starting to look up, so why not mark the occasion with some "hipster coding". After spending months and months working on a legacy WebForms codebase with lots of jQuery dotted around the place, I was given the opportunity to work on a super trendy Angular.js frontend and a ASP.NET Web API 2 backend featuring the Entity Framework. One of the requirements was a drag n' drop asynchronous file upload feature. I designed the whole of the backend from scratch using Web API 2 controller actions and Entity Framework Code First. So the whole of the upload functionality would have to piggyback onto the existing Code First model. The Angular.js code Danial Farid created the ng-file-upload directive - it's pretty lightweight and works very well. If you are using Yeoman to scaffold your Angular app, we need to install that (and the shim) using bower:
bower install ng-file-upload --save
bower install ng-file-upload-shim --save
The `--save` flag will insert the references to the dependencies into the script references `build` section of your `index.html` file automatically. Next up, inject the `ngFileUpload` directive into your application:
var app = angular.module('myAngularApp', ['ngFileUpload']);
The view We now need to include some boilerplate `HTML` code in the view where the file upload will be to get the file upload functionality up and running:
<div ngf-drop ngf-select ng-model="files" ng-model-rejected="rejFiles" ngf-drag-over-class="{accept:'dragover', reject:'dragover-err', delay:100}" class="drop-box ng-valid ng-dirty" ngf-multiple="true" ngf-allow-dir="true" ngf-accept="'image/*,application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/msword,application/vnd.ms-powerpoint,application/excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel,application/octet-stream,application/zip,application/x-zip'" ngf-change="upload(files,$event, rejFiles, anotherCustomParam)" ngf-drop-available="dropAvailable">
  <div ngf-no-file-drop class="ng-hide">File Drop not available</div>
  <div ng-show="dropAvailable" class=""><span>Drag files here to upload or <a href="#" style="text-decoration: underline;">browse</a></span></div>
</div>
Some notes: `ng-model` is required and it can be anything - it is just a reference that you will be able to access in your controller when doing the upload to the API. I've include about a million different content types / content type variants in the `ngf-accept` attribute so that'll provide you a good starting point for some of the file types that are supported by `ng-file-upload`. `ngf-drop-available` allows you to specific the element which should be your dropzone for the dragging and dropping of files onto your file uploader. `ng-model-rejected` allows you to specify a new object array where any files that are rejected by the file upload (i.e. because they do not fulfil the `ngf-accept` attributes' conditions) will be stored for future reference. You can loop over this in your view. `ngf-change` is the key thing here. Provide the name of a method in your controller that you want to be triggered when files are uploaded either by using the drag n' drop or the select file actions. `ngf-multiple` and `ngf-allow-dir` are also both useful and are pretty self-explanatory. The controller As specified in the `ngf-change` attribute, we should now include a new method in the controller with the method signature `upload(files, event, rejectedFiles, anotherCustomParam)`.
$scope.upload = function (files, event, rejectedFiles, anotherCustomParam) {

  for(var r in rejectedFiles){
      console.log(rejectedFiles[r]);
  }
  $scope.loading = true;

  if(anotherCustomParam !== undefined) {

    if (files && files.length) {

        for (var i = 0; i < files.length; i++) {

            (function (index) {

                var file = files[i];

                $scope.upload[index] = Upload.upload({
                    url: cfg.apiBase + 'Documents/Upload', // webapi url
                    method: 'POST',
                    data: { fileUploadObj: anotherCustomParam.ID },
                    file: file
                }).progress(function (evt) {
                    // set upload percentage
                    file.progress = parseInt(100.0 * evt.loaded / evt.total);
                }).success(function (data, status, headers, config) {
                    // file is uploaded successfully
                    file.complete = true;
                }).error(function (data, status, headers, config) {
                    // file failed to upload
                    file.error = true;
                    console.log(data);
                    console.log(status);
                });
            })(i);
        }
    }
  }
};
The method above loops over all of the files provided to the `ng-file-upload` control and lodges a `POST` request to the API controller action. The `progress` promise is called asynchronously, and the file object's `progress` property is updated in real-time. This is useful for displaying a real-time progress indicator. The HTML code in your view to display a progress indicator for each of the files uploaded will look something like this:
<div ng-repeat="file in files" class="file upload-file clearfix">
      <h3 class="file-title {{file.type}}" >{{file.name}}</h3>
      <span class="file-size">({{file.size / 1000 | number: 0 }}kb)</span>
      <div ng-show="!file.complete" class="progress-bar">
        <span class="meter" style="width: {{file.progress}}%;"></span>
      </div>
      <span class="status status-done" ng-show="file.complete">Done</span>
      <span class="status status-error" ng-show="file.error">Error</span>
      <span ng-switch on="globals.currentUser.isAdmin">
        <p class="file-description edit-area" ng-switch-when="true" editable-text="document.CaptionText" onaftersave="saveDocument(document)">{{document.CaptionText}}</p>
        <p class="file-description" ng-switch-default>{{document.CaptionText}}</p>
      </span>
</div>
To make everything look a bit prettier, here is some lovely `SCSS` for the progress bar:
.progress-bar {
  $base-border-color: gainsboro !default;
  $base-background-color: white !default;
  $base-border-radius: 3px !default;
  $action-color: #477DCA !default;
  $base-link-color: $action-color !default;
  $progress-border-color: $base-border-color;
  $progress-border: 1px solid $progress-border-color;
  $progress-meter-border-color: $action-color;
  $progress-meter-border: 1px solid darken($progress-meter-border-color, 15%);
  $progress-meter-color: $progress-meter-border-color;
  $progress-background: darken($base-background-color, 5);
  $progress-animation-duration: 0.7s;
  $progress-height: 20px;
  $progress-width: 150px;

  background-color: $progress-background;
  border: $progress-border;
  box-shadow: inset 0 0 3px 0 rgba(darken($progress-background, 50%), 0.15);
  border-radius: $base-border-radius;
  height: $progress-height;
  margin: 0 auto;
  padding: 2px;
  width: $progress-width;
  vertical-align: middle;
  float: right;
  display: inline-block;

  > span.meter {
    @include animation(progress $progress-animation-duration linear infinite);
    @include box-sizing(border-box);
    background-color: $progress-meter-color;
    @include background-image(linear-gradient(-45deg, rgba(255,255,255, 0.15) 25%,
                                                      transparent 25%,
                                                      transparent 50%,
                                                      rgba(255,255,255, 0.15) 50%,
                                                      rgba(255,255,255, 0.15) 75%,
                                                      transparent 75%));
    background-size: 40px 40px;
    background-repeat: repeat-x;
    border: $progress-meter-border;
    border-radius: $base-border-radius / 1.5;
    display: block;
    height: 100%;
    width: 60%;
  }
}

@-webkit-keyframes progress {
  0% {
    background-position: 0px 0px;
  }
  100% {
    background-position: 40px 0px;
  }
}

@-moz-keyframes progress {
  0% {
    background-position: 0px 0px;
  }
  100% {
    background-position: 40px 0px;
  }
}

@-ms-keyframes progress {
  0% {
    background-position: 0px 0px;
  }
  100% {
    background-position: 40px 0px;
  }
}

@-o-keyframes progress {
  0% {
    background-position: 0px 0px;
  }
  100% {
    background-position: 40px 0px;
  }
}

@keyframes progress {
  0% {
    background-position: 0px 0px;
  }
  100% {
    background-position: 40px 0px;
  }
}
The Web API element The controller action will look something like the below. I implemented a file upload to Amazon `S3` in the example below, but you are welcome to do something else with the posted files. I am using attribute routing as opposed to the default Web API 2 routing system as you may able to see from the method's use of the `Route` attribute. Some of the code in the method below is heavily redacted as there is a lot of business-specific logic that I needed to remove so I'm sure you'll be able to fix up anything that is missing / doesn't apply to you:
[HttpPost]
[Route("Documents/Upload")]
public async Task<HttpResponseMessage> UploadFile()
{
    var storageService = new StorageService();

    if (!Request.Content.IsMimeMultipartContent())
    {
        Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
    }

    var provider = GetMultipartProvider();
    var result = await Request.Content.ReadAsMultipartAsync(provider);

    // On upload, files are given a generic name like "BodyPart_26d6abe1-3ae1-416a-9429-b35f15e6e5d5"
    // so this is how you can get the original file name
    string originalFileName = GetDeserializedFileName(result.FileData.First());

    // uploadedFileInfo object will give you some additional stuff like file length,
    // creation time, directory name, a few filesystem methods etc..
    FileInfo uploadedFileInfo = new FileInfo(result.FileData.First().LocalFileName);

    // Generate a new GUID and shortguid to use for uploaded file name in s3

    Guid theFullGuid = Guid.NewGuid();

    // Do S3 upload stuff

    bool wasS3UploadSuccessful = false;
    string ext = string.Empty;

    try
    {
        // Get original file extension
        ext = Path.GetExtension(originalFileName);

        // Get the full path to the file uploded into App_Data. The file does not have extension at this point due to the way the multipart provider works. We retrieve this in the line above from the original file data object
        string path = System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/Temp/FileUploads/" + uploadedFileInfo.Name); // Ensure that this folder hierarchy exists!

        // Below lines read the file in App_Data into the FileStream using the byte[] buffer
        FileStream MyFileStream = new FileStream(path, FileMode.Open, FileAccess.Read);

        long FileSize = MyFileStream.Length;

        byte[] Buffer = new byte[(int)FileSize + 1];

        MyFileStream.Read(Buffer, 0, (int)MyFileStream.Length);

        storageService.UploadFile("bucket-name", theFullGuid + ext.ToLower(), MyFileStream);

        // Close the file stream
        MyFileStream.Close();

        // Set S3 success flag
        wasS3UploadSuccessful = true;
    }
    catch (Exception ex)
    {
        // Set the s3 upload wasSuccess flag
        wasS3UploadSuccessful = false;

        exceptions.Add(ex);
    }

    // If the S3 file upload was a raging success, then save the corresponding entry in the Documents table.
    if (wasS3UploadSuccessful)
    {
        return Request.CreateResponse(HttpStatusCode.Ok);
    }
    return Request.CreateResponse(HttpStatusCode.BadRequest);
}
The various methods needed to setup a `MultiPartFormDataStreamProvider`, upload the files to a temporary directory in the root of your ASP.NET site and to grab the original file name from the multi part upload can be found below:
#region FileUploadHelpers
public static MultipartFormDataStreamProvider GetMultipartProvider()
{
    var uploadFolder = "~/App_Data/Temp/FileUploads";
    var root = HttpContext.Current.Server.MapPath(uploadFolder);
    Directory.CreateDirectory(root);
    return new MultipartFormDataStreamProvider(root);
}
private string GetDeserializedFileName(MultipartFileData fileData)
{
    var fileName = GetFileName(fileData);
    return JsonConvert.DeserializeObject(fileName).ToString();
}
private string GetFileName(MultipartFileData fileData)
{
    return fileData.Headers.ContentDisposition.FileName;
}
#endregion
The Amazon S3 `StorageService` class can be found below. Ensure you change the S3 region to match that of your bucket
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using Amazon;
using Amazon.IdentityManagement.Model;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.S3.Transfer;

namespace API.AmazonS3
{
    public class StorageService
    {
        private IAmazonS3 client = null;

        public StorageService()
        {
            string accessKey = ConfigurationManager.AppSettings["AWSAccessKey"];

            string secretKey = ConfigurationManager.AppSettings["AWSSecretKey"];

            if (this.client == null)
            {
                this.client = Amazon.AWSClientFactory.CreateAmazonS3Client(accessKey, secretKey, RegionEndpoint.EUWest1); //	The EU West (Ireland) endpoint. 
            }
        }
        #region Upload
        public bool UploadFile(string awsBucketName, string key, Stream stream)
        {
            var uploadRequest = new TransferUtilityUploadRequest
            {
                InputStream = stream,
                BucketName = awsBucketName,
                CannedACL = S3CannedACL.PublicRead,
                Key = key
            };

            var fileTransferUtility = new TransferUtility(this.client);

            fileTransferUtility.Upload(uploadRequest);

            return true;
        }

        #endregion

        #region Download
        public Stream DownloadToStream(string fileName, string bucketName)
        {
           
            var request = new GetObjectRequest
            {
                BucketName = bucketName,
                Key = fileName
            };

            var response = client.GetObject(request);

            return response.ResponseStream;
        }
        public byte[] StreamToBytes(Stream input)
        {
            var buffer = new byte[16 * 1024];
            using (var ms = new MemoryStream())
            {
                int read;
                while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
                {
                    ms.Write(buffer, 0, read);
                }
                return ms.ToArray();
            }
        }


        #endregion
    }
}
And that is it! Everything should work out of the box. There may be some modifications needed to your API's web.config file, as well as some Cross Origin Resource Sharing (CORS) related grief that you will encounter. I sorted this by creating my own custom CORS policy provider, and injecting a `CorsPolicyProviderFactory` instance into my API startup class as below: The `CorsPolicyProvider` implementation I don't think that you'll need to make any / many modifications to the below code. If you're using grunt / yeoman to scaffold your Angular app, then I think your port will be 9000 as in the example below, but if that isn't the case, then change the port number to match that of your Angular app.
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Cors;

namespace API.MaCorsPolicy
{
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)]
    public class MyCorsPolicyProvider : Attribute, ICorsPolicyProvider
    {
        private readonly System.Web.Cors.CorsPolicy _policy;

        public MyCorsPolicyProvider()
        {
            // Create a CORS policy.
                _policy = new System.Web.Cors.CorsPolicy
                {
                    AllowAnyMethod = true,
                    AllowAnyHeader = true
                };

            // Add allowed origins.
            _policy.Origins.Add("http://localhost:9000");
        }

        public Task<System.Web.Cors.CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            return Task.FromResult(_policy);
        }
    }
}
The `ICorsPolicyProviderFactory` implementation The below code is simply a factory class to create instances of the `MyCorsPolicyProvider` class when needed. Simple as that.
using System.Net.Http;
using System.Web.Http.Cors;
using API.MaCorsPolicy;

namespace API.CorsPolicy
{
    public class CorsPolicyFactory : ICorsPolicyProviderFactory
    {
        readonly ICorsPolicyProvider _provider = new MyCorsPolicyProvider();

        public ICorsPolicyProvider GetCorsPolicyProvider(HttpRequestMessage request)
        {
            return _provider;
        }
    }
}
And finally, the `WebApiConfig` class using the `config.SetCorsPolicyProviderFactory` method:
namespace API
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API routes
            config.MapHttpAttributeRoutes();

            config.SetCorsPolicyProviderFactory(new CorsPolicyFactory());
            config.EnableCors();
        }
    }
}
Any questions, then feel free to ask in the comments below.