Thursday, August 28, 2014

How to Directly Upload Files to a Amazon S3 Without Saving Locally in ASP.NET Web API

In this post let’s see how we can directly upload files to Amazon S3 which are sent through ASP.NET Web API service via a form object by client side. The nice thing here is we are not going to save the files locally while the upload is happening. File will be directly uploaded to Amazon S3.

Let’s go by a example. My client application is an AngularJS application. Here I am not going to write about how to consume ASP.NET Web API services from AngularJS application. Sometimes back I wrote an article about AngularJS Consuming ASP.NET Web API RESTful Services, you can refer that post for more information on AngularJS consuming ASP.NET Web API services.

I have modified the sample in AngularJS Consuming ASP.NET Web API RESTful Services post to send a POST request to ASP.NET Web API service, and the request contains the form object which contains the file to upload.

Here is my view template file. I have a file-select field to open the folder browser.
<div ng-controller="homeController">
    <h1>{{message}}</h1>
    <label for="">Image</label>
    <input type="file" ng-file-select="onFileSelect($files)" accept="Image/*">
</div>

In my AngularJS Controller, I have the following.
'use strict';

app.controller('homeController', ['$scope', '$upload', function ($scope, $upload) {
    $scope.message = "Now viewing home!"; 

    $scope.fileUploadData = {
        UserName: "Jaliya Udagedara"
    };
 
    $scope.onFileSelect = function ($files) {
        for (var i = 0; i < $files.length; i++) {
            var file = $files[i];
            $scope.upload = $upload.upload({
                url: '../api/employees/upload',
                method: 'POST',
                data: { UserName: $scope.fileUploadData.UserName },
                file: file
            }).progress(function (evt) {
                console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
            }).success(function (data, status, headers, config) {
                console.log(data);
            }).error(function (data) {
                console.log(data);
            });
        }
    };
}]);

If you are not familiar with AngularJS, you can just ignore the above part. Here basically what I have done is sending a POST request to my ASP.NET Web API service including the file I need to upload and some additional data.

Now let’s move into my ASP.NET Web API project. There I have a API Controller named “EmployeesController” and there I have a action named “Upload”.
[HttpPost]
[Route("Employees/Upload")]
public async Task<HttpResponseMessage> Upload()
{
    StorageService storageService = new StorageService();
 
    if (!Request.Content.IsMimeMultipartContent())
    {
        this.Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
    }
 
    InMemoryMultipartStreamProvider provider = await Request.Content.ReadAsMultipartAsync<InMemoryMultipartStreamProvider>(new InMemoryMultipartStreamProvider());
 
    NameValueCollection formData = provider.FormData;
    string userName = formData["UserName"];
 
    IList<HttpContent> files = provider.Files;
 
    HttpContent file = files[0];
    Stream fileStream = await file.ReadAsStreamAsync();
 
    storageService.UploadFile("your bucketname", userName, fileStream);

    string preSignedUrl = storageService.GeneratePreSignedURL("your bucketname", userName, 3000);
 
    return this.Request.CreateResponse(HttpStatusCode.OK, new { preSignedUrl });
}
In here first I am checking whether the requests’ content is MIME multipart content. If yes, I am reading all body parts within a MIME multipart message and produces a result of type InMemoryMultipartStreamProvider. In here InMemoryMultipartStreamProvider is my implementation of abstract class MultipartStreamProvider. There I have overrided the GetStream method just to return me a MemoryStream. The implementation of it is totally based on MultipartFormDataStreamProvider and MultipartFileStreamProvider. The MultipartFormDataStreamProvider has two constructors, one taking a root path and other taking root path and a buffer size. If you look at the implementation of  MultipartFormDataStreamProvider, inside the GetStream method the files are saving locally. Here in my implementation I have removed them, as I don’t want that to happen.

After getting the stream of the file, I have a helper class named “StorageService”, which is used to upload the file into Amazon S3. Amazon S3 SDK for .NET can upload files via a stream. After uploading is completed, I am returning the uploaded files’ Url.

InMemoryMultipartStreamProvider.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
 
public class InMemoryMultipartStreamProvider : MultipartStreamProvider
{
    private NameValueCollection formData = new NameValueCollection();
    private List<HttpContent> fileContents = new List<HttpContent>();
    private Collection<bool> isFormData = new Collection<bool>();
 
    public NameValueCollection FormData
    {
        get { return this.formData; }
    }

    public List<HttpContent> Files
    {
        get { return this.fileContents; }
    }
 
    public override Stream GetStream(HttpContent parent, HttpContentHeaders headers)
    {
        ContentDispositionHeaderValue contentDisposition = headers.ContentDisposition;
        if (contentDisposition != null)
        {
            this.isFormData.Add(string.IsNullOrEmpty(contentDisposition.FileName));
 
            return new MemoryStream();
        }
        throw new InvalidOperationException(string.Format("Did not find required '{0}' header field in MIME multipart body part.", "Content-Disposition"));
    }
 
    public override async Task ExecutePostProcessingAsync()
    {
        for (int index = 0; index < Contents.Count; index++)
        {
            if (this.isFormData[index])
            {
                HttpContent formContent = Contents[index];
 
                ContentDispositionHeaderValue contentDisposition = formContent.Headers.ContentDisposition;
                string formFieldName = UnquoteToken(contentDisposition.Name) ?? string.Empty;
 
                string formFieldValue = await formContent.ReadAsStringAsync();
                this.FormData.Add(formFieldName, formFieldValue);
            }
            else
            {
                this.fileContents.Add(this.Contents[index]);
            }
        }
    }
 
    private static string UnquoteToken(string token)
    {
        if (string.IsNullOrWhiteSpace(token))
        {
            return token;
        }
 
        if (token.StartsWith("\"", StringComparison.Ordinal) && token.EndsWith("\"", StringComparison.Ordinal) && token.Length > 1)
        {
            return token.Substring(1, token.Length - 2);
        }
 
        return token;
    }
}

StorageService.cs
using Amazon;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.S3.Transfer;
using System;
using System.Configuration;
using System.IO;
 
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.APSoutheast1);
        }
    }
    
    public bool UploadFile(string awsBucketName, string key, Stream stream)
    {
        var uploadRequest = new TransferUtilityUploadRequest
        {
            InputStream = stream,
            BucketName = awsBucketName,
            CannedACL = S3CannedACL.AuthenticatedRead,
            Key = key
        };
 
        TransferUtility fileTransferUtility = new TransferUtility(this.client);
        fileTransferUtility.Upload(uploadRequest);
        return true;
    }
 
    public string GeneratePreSignedURL(string awsBucketName, string key, int expireInSeconds)
    {
        string urlString = string.Empty;
        GetPreSignedUrlRequest request = new GetPreSignedUrlRequest
        {
            BucketName = awsBucketName,
            Key = key,
            Expires = DateTime.Now.AddSeconds(expireInSeconds)
        };
 
        urlString = this.client.GetPreSignedURL(request);
        return urlString;
    }
}

Now when I run the project, I am getting the following. When I click on the choose file button, folder explorer opens and I can select a image file there.

SNAGHTML6538b830
File Upload
Then when I click on Open, image starts to get uploading. Once upload is completed, I can see my uploaded file in the Amazon S3 storage.

image
Uploaded File
I am uploading the sample to my OneDrive. Enjoy!


Happy Coding.

Regards,
Jaliya

Thursday, August 21, 2014

Visual C# Technical Guru - July 2014

Became second in Visual C# Technical Guru Awards for the month of July, 2014.

The TechNet Guru Awards celebrate the technical articles on Microsoft TechNet.

Post in WikiNinjas Official Blog,

image
Visual C# Technical Guru – July 2014
Happy Coding.

Regards,
Jaliya

Sunday, August 10, 2014

AngularJS Consuming ASP.NET Web API RESTful Services

In this post let’s see how we can use AngularJS to consume RESTful ASP.NET Web API services. Please note that I am not going to go deeper in ASP.NET Web API here. Even though ASP.NET Web API is related to our subject today, it should be discussed under it’s own separate topic. Today I assume that you have some knowledge in working with ASP.NET Web API. (If you are not, go and have some quick lessons on Learn About ASP.NET Web API)

In one of my previous posts, I wrote about Creating an Empty ASP.NET Project Powered by AngularJS using Visual Studio and for this post I am going to use that sample as the base and modify from there.

So now I have a ASP.NET Empty project all configured with AngularJS. Let’s add a new project to the solution. I am right clicking on the solution and clicking on Add –> New Project.

image
New Project
I am giving a name and clicking on OK. From the next dialog window, I am selecting Web API and clicking on OK.

image
Web API
Once the project is created, I am going to do some configuration changes in the project.

First right click on the AngularJSSample project and click on Properties. There under Web tab, copy the Project Url.

image
Project URL
Now right click on the Web API project and click on Properties. There on the Web tab change the Project Url as follows.

image
Project URL
If you are prompted for creating a Virtual Directory, Click on OK. Basically what I have done here is making our ASP.NET Web API run on the same host under a different virtual directory. For example, if the AngularJSSample project Url is “http://localhost:2222”, the Web API project Url will be “http://localhost:2222/api”. 

Now let’s start modifying the Web API project. First Let’s add a my favorite “Employee” model to the project. Right click on the “Model” folder and add a new class named “Employee”.
using System.Collections.Generic;

namespace WebAPI.Models
{
    public class Employee
    {
        public int EmployeeId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public static List<Employee> GetEmployees()
        {
            return new List<Employee>()
            {
                new Employee()
                {
                    EmployeeId=1,
                    FirstName="Jaliya",
                    LastName="Udagedara"
                },
                new Employee()
                {
                    EmployeeId=2,
                    FirstName="John",
                    LastName="Smith"
                },
                new Employee()
                {
                    EmployeeId=3,
                    FirstName="Jane",
                    LastName="Smith"
                }
            };
        }
    }
}
Now  in the Web API project, I am clicking on the Controllers folder and Add –> Controller. There I am selecting Web API 2 Controller – Empty.

image
Web API 2 Controller - Empty
I am giving the Controller names as “EmployeesController”.

image
EmployeesController
Here for demonstration purposes, my “EmployeesController” would be exposing data which is returned by “Employee.GetEmployees()” method. In real implementations, you can be retrieving data from databases etc.

I am writing the following method to return all employees.
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using WebAPI.Models;

namespace WebAPI.Controllers
{
    public class EmployeesController : ApiController
    {
        [HttpGet]
        [Route("employees")]
        public IEnumerable<Employee> GetEmployees()
        {
            return Employee.GetEmployees().AsEnumerable();
        }
    }
}
Here I have decorated my Action with HttpGet and Route attributes. These attributes are for saying when you request a HttpGet on "http://localhost:2222/api/employees", call the GetEmployees() method. Here you don't actually have to decorate the method with Route attribute unless you want to mention a custom route. Default route would be Controllers name without the Controller part of it (EmployeesController -> Route would be "http://localhost:2222/api/employees"). These of course, comes with ASP.NET Web API theories and practices.

Now run the Web API project, and navigate to Employees controller through the url. If all is good, you should be able to see the following.

image
Result
Now our task for today is consuming the above ASP.NET Web API REST service using AngularJS. In my Web API project, I will have a service with a HttpGet which will return me all the employees.

Let’s move into the AngularJSSample project. I am going to show all the employees in my home page. So let’s go to “App –> controllers” and start modifying the “homeController.js”.
'use strict';

app.controller('homeController', ['$scope', 'employeeService', function ($scope, employeeService) {
    $scope.message = "Now viewing home!";
 
    $scope.employees = {
        EmployeeId: "",
        FirstName: "",
        LastName: ""
    };
 
    employeeService.getEmployees($scope);
}])

app.service('employeeService', ['$http', function ($http) {
    this.getEmployees = function ($scope) {
        return $http({
            method: "GET",
            url: "../api/employees",
            headers: { 'Content-Type': 'application/json' }
        }).success(function (data) {
            $scope.employees = data;
            console.log(data);
        }).error(function (data) {
            console.log(data);
        });;
    };
}]);
Here I have used AngularJS Service named “employeeService” to call ASP.NET Web API. There I have a method named getEmployees() which is responsible for calling the Web API and setting the values in $scope object which was passed in as a parameter.

In my “homeController”, I am injecting the “employeeService” and making use of it. Then let’s modify the home template which was located in “App/views”, to show the values retrieved from the service.
<div ng-controller="homeController">
    <h1>{{message}}</h1>
 
    <div ng-repeat="employee in employees">
        <label>{{employee.EmployeeId}}</label>
        <label>{{employee.FirstName}}</label>
        <label>{{employee.LastName}}</label>
    </div>
</div>
Here I have used the ng-repeat directive which kind of acts as a foreach in C#. Now when I run the "AngularJSSample" project, I can see the following.

image
Result

Here I have only wrote about AngularJS querying values from ASP.NET Web API through HttpGet. It’s up for you to discover how AngularJS makes other operations such as HttpPost etc. on RESTful services.

I am uploading the sample to OneDrive.


Happy Coding.

Regards,
Jaliya