Upload and download files using HTML5 File Uploader Control and AngularJS

Introduction

 
In classic ASP.Net, uploading a physical file using the file upload control is very easy. But when we need to do the same type of work in a normal HTML project using a client-side script like AngularJs and the Web API, there is some special process required. This article explains how to upload a file using AngularJs. Also, during the upload process, we will copy the file from its original location to a specified location. Then we can also download that file from that specified location.
 
For the preceding purposes, we create two projects in Visual Studio.
  • One project is a blank website named FileUploader.
  • The second project is an empty Web API project named FileUploaderAPI
Now, in the web site project, we create the following 3 folders namely:
  • HTML
  • Script
  • UserScript 
Now we add an Angular.min.js file within the Script folder. This file can be easily downloaded from the Nuget Gallery or from the Angular website.
 
Now we will add a HTML file to the HTML folder named FileUploader.html and write the following HTML code there.
  1. <!DOCTYPE html>  
  2. <html xmlns="http://www.w3.org/1999/xhtml">  
  3. <head>  
  4.     <title>File Uploader</title>  
  5.     <script src="../Script/angular1.3.8.js"></script>  
  6.     <script src="../Script/angular-route.js"></script>  
  7.     <script src="../UserScript/MyApp.js"></script>  
  8.     <script src="../UserScript/FileUploder.js"></script>  
  9.     <>  
  10.         .percent {  
  11.             position: absolute;  
  12.             width: 300px;  
  13.             height: 14px;  
  14.             z-index: 1;  
  15.             text-align: center;  
  16.             font-size: 0.8em;  
  17.             color: white;  
  18.         }  
  19.   
  20.         .progress-bar {  
  21.             width: 300px;  
  22.             height: 14px;  
  23.             border-radius: 10px;  
  24.             border: 1px solid #CCC;  
  25.             background-image: -webkit-gradient(linear, left top, left bottom, from(#6666cc), to(#4b4b95));  
  26.             border-image: initial;  
  27.         }  
  28.   
  29.         .uploaded {  
  30.             padding: 0;  
  31.             height: 14px;  
  32.             border-radius: 10px;  
  33.             background-image: -webkit-gradient(linear, left top, left bottom, from(#66cc00), to(#4b9500));  
  34.             border-image: initial;  
  35.         }  
  36.     </>  
  37. </head>  
  38. <body ng-app="MyApp" ng-controller="FileUploder">  
  39.     <div>  
  40.         <table ="width:100%;border:solid;">  
  41.             <tr>  
  42.                 <td>Select File</td>  
  43.                 <td>  
  44.                     <input type="file" ng-model-instant id="fileToUpload" onchange="angular.element(this).scope().setFiles(this)" />  
  45.                 </td>  
  46.             </tr>  
  47.             <tr>  
  48.                 <td>File Size</td>  
  49.                 <td>  
  50.                     <div ng-repeat="file in files.slice(0)">  
  51.                         <span ng-switch="file.size > 1024*1024">  
  52.                             <span ng-switch-when="true">{{file.size / 1024 / 1024 | number:2}} MB</span>  
  53.                             <span ng-switch-default>{{file.size / 1024 | number:2}} kB</span>  
  54.                         </span>  
  55.                     </div>  
  56.                 </td>  
  57.             </tr>             
  58.             <tr>  
  59.                 <td>  
  60.                     File Attach Status  
  61.                 </td>  
  62.                 <td>{{AttachStatus}}</td>  
  63.             </tr>  
  64.             <tr>  
  65.                 <td>  
  66.                     <input type="button" value="Upload" ng-click="fnUpload();" />  
  67.                 </td>  
  68.                 <td>  
  69.                     <input type="button" value="DownLoad" ng-click="fnDownLoad();" />  
  70.                 </td>  
  71.             </tr>  
  72.         </table>  
  73.     </div>  
  74. </body>  
  75. </html>  
Now in the preceding code, we have taken the reference of the two JavaScript files:
 
MyApp.Js and FileUploader.JS.
 
Now, add a JavaScript file within the UserScript folder named MyApp.Js and add the following code.
  1. var MyApp = angular.module('MyApp', []);  
Again add another JavaScript file within the same folder named FileUploader.JS and define the controller in that file as in the following.
  1. MyApp.controller("FileUploder", ['$scope''$http''$timeout''$window',    
  2.         function ($scope, $http, $location, $timeout, $window) {    
  3.             $scope.AttachStatus = "";    
  4.     }    
  5.     ]);    
Now depending on the normal functionality of file upload control of HTML, when we click on the Choose File button, it opens the file open dialog and allows us to select file. Now our objective is, after selecting the file, it will automatically read the file and show the file size in the page. For this purpose, we called the onchange event of the file upload control and written the following code.
  1. $scope.setFiles = function (element) {  
  2.             $scope.$apply(function (scope) {  
  3.                 $scope.AttachStatus = "";  
  4.                 $scope.files = []  
  5.                 for (var i = 0; i < element.files.length; i++) {  
  6.                     $scope.files.push(element.files[i])  
  7.                 }  
  8.                 $scope.progressVisible = false  
  9.             });  
  10.         }
This function takes the instance of the control as an argument and updates the scope with the file detail information, such as file name, file size in bytes and so on.
 
Now our next objective is to upload the file using the Web API so that this specific file can be copied and saved in a specific location. For this, we already created a button name Upload. We need to click on this button for uploading. When we click this button, it will call an AngularJs function that internally redirects to the Web API controller to copy and save the file into a specific location. (Here I am using Temporary Internet Files folder for the location.)
 
Now write the following code first into the fileupload.js file.
  1. $scope.fnUpload = function () {  
  2.             var fd = new FormData()  
  3.             for (var i in $scope.files) {  
  4.                 fd.append("uploadedFile", $scope.files[i])  
  5.             }  
  6.             var xhr = new XMLHttpRequest();  
  7.             xhr.addEventListener("load", uploadComplete, false);  
  8.             xhr.open("POST""http://localhost:53154/api/FileUploader/AttachFile"true);  
  9.             $scope.progressVisible = true;  
  10.             xhr.send(fd);  
  11.         }  
  12.   
  13.         function uploadComplete(evt) {  
  14.             $scope.progressVisible = false;  
  15.             if (evt.target.status == 201) {  
  16.                 $scope.FilePath = evt.target.responseText;  
  17.                 $scope.AttachStatus = "Upload Done";  
  18.                 alert($scope.FilePath);  
  19.             }  
  20.             else {  
  21.                 $scope.AttachStatus = evt.target.responseText;  
  22.             }  
  23.         }  
In the fnUpload button, it creates an instance of FormData object and stores the file information within the FormData and sends the data to the webapi as a XMLHttpRequest. And the uploadComplete function checks if the Web API returns a status code 201 (in other words success) or not.
 
Now for the Web API code. For that, we will add a controller file within the controller folder named FileUploaderController and write the following code in that file.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.IO;  
  4. using System.Linq;  
  5. using System.Net;  
  6. using System.Net.Http;  
  7. using System.Net.Http.Headers;  
  8. using System.Web;  
  9. using System.Web.Http;  
  10.   
  11. namespace FileUploader.Controllers  
  12. {  
  13.     public class FileUploaderController : ApiController  
  14.     {  
  15.         [HttpPost]  
  16.         public HttpResponseMessage AttachFile()  
  17.         {  
  18.             HttpResponseMessage result = null;  
  19.             var httpRequest = HttpContext.Current.Request;  
  20.             if (httpRequest.Files.Count > 0)  
  21.             {  
  22.                 var docfiles = new List<string>();  
  23.                 foreach (string file in httpRequest.Files)  
  24.                 {  
  25.                     var postedFile = httpRequest.Files[file];  
  26.                     string filePath = Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.InternetCache), postedFile.FileName));  
  27.                     postedFile.SaveAs(filePath);  
  28.   
  29.                     docfiles.Add(filePath);  
  30.                 }  
  31.                 result = Request.CreateResponse(HttpStatusCode.Created, docfiles);  
  32.             }  
  33.             else  
  34.             {  
  35.                 result = Request.CreateResponse(HttpStatusCode.BadRequest);  
  36.             }  
  37.             return result;  
  38.         }  
  39. }  
  40. }  
Now our file upload part is complete. Now, if we run the project and select a file and click on the upload button it will display the file location in an alert box. We can check that the file is physically there.
 
Now to download a file, we already created a button named DownLoad in the HTML page. Now we will write the code for this download button in the fileuploader.js file.
  1. $scope.fnDownLoad = function () {  
  2.            debugger;  
  3.            $scope.FileExt = $scope.files[0].name.substr($scope.files[0].name.length - 4);  
  4.            $scope.GenerateFileType($scope.FileExt);  
  5.            $scope.RenderFile();  
  6.        }  
  7.   
  8.        $scope.RenderFile = function () {  
  9.            var s = "http://localhost:53154/api/FileUploader/DownLoadFile?"  
  10.               + "FileName=" + $scope.files[0].name  
  11.               + "&fileType=" + $scope.FileType;  
  12.            $window.open(s);  
  13.        }  
  14.   
  15.        $scope.GenerateFileType = function (fileExtension) {  
  16.            switch (fileExtension.toLowerCase()) {  
  17.                case "doc":  
  18.                case "docx":  
  19.                    $scope.FileType = "application/msword";  
  20.                    break;  
  21.                case "xls":  
  22.                case "xlsx":  
  23.                    $scope.FileType = "application/vnd.ms-excel";  
  24.                    break;  
  25.                case "pps":  
  26.                case "ppt":  
  27.                    $scope.FileType = "application/vnd.ms-powerpoint";  
  28.                    break;  
  29.                case "txt":  
  30.                    $scope.FileType = "text/plain";  
  31.                    break;  
  32.                case "rtf":  
  33.                    $scope.FileType = "application/rtf";  
  34.                    break;  
  35.                case "pdf":  
  36.                    $scope.FileType = "application/pdf";  
  37.                    break;  
  38.                case "msg":  
  39.                case "eml":  
  40.                    $scope.FileType = "application/vnd.ms-outlook";  
  41.                    break;  
  42.                case "gif":  
  43.                case "bmp":  
  44.                case "png":  
  45.                case "jpg":  
  46.                    $scope.FileType = "image/JPEG";  
  47.                    break;  
  48.                case "dwg":  
  49.                    $scope.FileType = "application/acad";  
  50.                    break;  
  51.                case "zip":  
  52.                    $scope.FileType = "application/x-zip-compressed";  
  53.                    break;  
  54.                case "rar":  
  55.                    $scope.FileType = "application/x-rar-compressed";  
  56.                    break;  
  57.            }  
  58.        }  
In the preceding code, we first created the file extension from the file name and then set the file MIME type depending on the file extension. Then we will again call the Web API get method to download the file where we the file name and file extension as parameter.
 
The file download method is as in the following.
  1. [HttpGet]  
  2.         public HttpResponseMessage DownLoadFile(string FileName, string fileType)  
  3.         {  
  4.             Byte[] bytes = null;  
  5.             if (FileName != null)  
  6.             {  
  7.                 string filePath = Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.InternetCache), FileName));  
  8.                 FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);  
  9.                 BinaryReader br = new BinaryReader(fs);  
  10.                 bytes = br.ReadBytes((Int32)fs.Length);  
  11.                 br.Close();  
  12.                 fs.Close();  
  13.             }  
  14.   
  15.             HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);  
  16.             System.IO.MemoryStream stream = new MemoryStream(bytes);  
  17.             result.Content = new StreamContent(stream);  
  18.             result.Content.Headers.ContentType = new MediaTypeHeaderValue(fileType);  
  19.             result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")  
  20.             {  
  21.                 FileName = FileName  
  22.             };  
  23.             return (result);  
  24.         }  
This get method actually reads the file from its physical location (where the file was saved during the upload) and then converts the file into a byte array using file stream reader. Then return the byte content as a HttpResponseMessage to the browser so that the browser can download that file directly.
 
The following is the complete code of the Fileuploader.js file.
  1. MyApp.controller("FileUploder", ['$scope''$http''$timeout''$window',  
  2.     function ($scope, $http, $location, $timeout, $window) {  
  3.         $scope.AttachStatus = "";  
  4.   
  5.         $scope.fnUpload = function () {  
  6.             var fd = new FormData()  
  7.             for (var i in $scope.files) {  
  8.                 fd.append("uploadedFile", $scope.files[i])  
  9.             }  
  10.             var xhr = new XMLHttpRequest();  
  11.             xhr.addEventListener("load", uploadComplete, false);  
  12.             xhr.open("POST""http://localhost:53154/api/FileUploader/AttachFile"true);  
  13.             $scope.progressVisible = true;  
  14.             xhr.send(fd);  
  15.         }  
  16.   
  17.         function uploadComplete(evt) {  
  18.             $scope.progressVisible = false;  
  19.             if (evt.target.status == 201) {  
  20.                 $scope.FilePath = evt.target.responseText;  
  21.                 $scope.AttachStatus = "Upload Done";  
  22.                 alert($scope.FilePath);  
  23.             }  
  24.             else {  
  25.                 $scope.AttachStatus = evt.target.responseText;  
  26.             }  
  27.         }  
  28.   
  29.         $scope.fnDownLoad = function () {  
  30.             debugger;  
  31.             $scope.FileExt = $scope.files[0].name.substr($scope.files[0].name.length - 4);  
  32.             $scope.GenerateFileType($scope.FileExt);  
  33.             $scope.RenderFile();  
  34.         }  
  35.   
  36.         $scope.RenderFile = function () {  
  37.             var s = "http://localhost:53154/api/FileUploader/DownLoadFile?"  
  38.                + "FileName=" + $scope.files[0].name  
  39.                + "&fileType=" + $scope.FileType;  
  40.             $window.open(s);  
  41.         }  
  42.   
  43.         $scope.GenerateFileType = function (fileExtension) {  
  44.             switch (fileExtension.toLowerCase()) {  
  45.                 case "doc":  
  46.                 case "docx":  
  47.                     $scope.FileType = "application/msword";  
  48.                     break;  
  49.                 case "xls":  
  50.                 case "xlsx":  
  51.                     $scope.FileType = "application/vnd.ms-excel";  
  52.                     break;  
  53.                 case "pps":  
  54.                 case "ppt":  
  55.                     $scope.FileType = "application/vnd.ms-powerpoint";  
  56.                     break;  
  57.                 case "txt":  
  58.                     $scope.FileType = "text/plain";  
  59.                     break;  
  60.                 case "rtf":  
  61.                     $scope.FileType = "application/rtf";  
  62.                     break;  
  63.                 case "pdf":  
  64.                     $scope.FileType = "application/pdf";  
  65.                     break;  
  66.                 case "msg":  
  67.                 case "eml":  
  68.                     $scope.FileType = "application/vnd.ms-outlook";  
  69.                     break;  
  70.                 case "gif":  
  71.                 case "bmp":  
  72.                 case "png":  
  73.                 case "jpg":  
  74.                     $scope.FileType = "image/JPEG";  
  75.                     break;  
  76.                 case "dwg":  
  77.                     $scope.FileType = "application/acad";  
  78.                     break;  
  79.                 case "zip":  
  80.                     $scope.FileType = "application/x-zip-compressed";  
  81.                     break;  
  82.                 case "rar":  
  83.                     $scope.FileType = "application/x-rar-compressed";  
  84.                     break;  
  85.             }  
  86.         }  
  87.   
  88.         $scope.setFiles = function (element) {  
  89.             $scope.$apply(function (scope) {  
  90.                 $scope.AttachStatus = "";  
  91.                 $scope.files = []  
  92.                 for (var i = 0; i < element.files.length; i++) {  
  93.                     $scope.files.push(element.files[i])  
  94.                 }  
  95.                 $scope.progressVisible = false  
  96.             });  
  97.         }  
  98.   
  99.     }  
  100. ]);  
There is one thing we need to remember. In a normal scenario, Visual Studio or ASP.NET allows us to upload a file of a maximum size of 4 MB. If we want to upload a larger file then we need to change the web.config file as in the following.
  1. <httpRuntime targetFramework="4.5" maxRequestLength="104857600"  />    
  2.     
  3. <security>    
  4.       <requestFiltering>    
  5.         <requestLimits maxAllowedContentLength="104857600" maxQueryString="104857600"/>    
  6.       </requestFiltering>    
  7.     </security>    
First, provide a maxRequestLength value in byte format that we want.
 
Secondly, add the requestFiletering tab within security as specified above.
 
Now, our task is complete and we will run the project. The following is the final output.
 
 
Figure 1: Output