Source Code
Introduction
From last few days i am trying upload multiple image with Angular and ASP.net MVC. i also want to track the individual file's upload progress. I google a lot. Found lot of code but some do not work. After 6 days hard work at last i found the proper way. I did not get one place where all the things grather.(may be my searching capability is low). So here i aim to grather all the thing in one place that may help you to learn the process of uploading multiple file using angularJS and asp.net MVC. Lets Start
Description
I assume that you have konowledge on how to use AngularJS. I also assume that you have knowledge on Twitter Bootstrap ,Font Awesome. HTML5 and CSS3. I will proced step by step
Step 1: Prepare HTML file and Show Selected File Information
we use HTML input tag to input multiple/single file upload. The HTML file is as follows
<html ng-app="AgApp">
<head>
<meta name="viewport" content="width=device-width" />
<title>ImageUploadMultiple</title>
<link href="~/App_Content/CSS/bootstrap.min.css" rel="stylesheet" />
<link href="~/App_Content/CSS/font-awesome.min.css" rel="stylesheet" />
</head>
<body>
<div class="col-md-12">
<h2>Image/File Upload With Angular::</h2>
</div>
<div ng-controller="ImageUploadMultipleCtrl">
<div class="col-md-12" style="text-align:center;margin-bottom:10px;">
<input type="file" id="file" name="file" multiple onchange="angular.element(this).scope().setFile(this)" accept="image/*" class="btn btn-warning" />
</div>
<div class="col-md-12">
<button ng-click="UploadFile()" class="btn btn-primary" >Upload File</button>
</div>
<div class="col-md-12" style="padding-top:10px;">
<div class="col-md-7">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>File Name</th>
<th>File Type</th>
<th>File Size</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="file in fileList">
<td>{{file.file.name}}</td>
<td>{{file.file.type}}</td>
<td>{{file.file.size}}</td>
<td>
<div id="{{'P'+$index}}">
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!---->
<script src="~/App_Content/JS/jquery-1.7.2.min.js"></script>
<script src="~/App_Content/JS/jquery.unobtrusive-ajax.min.js"></script>
<!---->
<script src="~/App_Content/JS/angular.min.js"></script>
<!---->
<script src="~/App_Content/JS/Practical/Module.js"></script>
<!---->
<script src="~/App_Content/JS/Practical/ImageUploadMultipleController.js"></script>
</body>
</html>
- ng-app="AgApp" we bootstrap an Angular application
-
ng-controller="ImageUploadMultipleCtrl" specifiy the controller for this page that contain the code for show slected file information and file upload function
-
<input type="file" id="file" name="file" multiple onchange="angular.element(this).scope().setFile(this)"> enable us to input multiple file. And the onchange event we call setFile function which is an angular function. This function collects all files you selected and put into a $scope variable name fileList.
-
as soon as file information save at fileList. we loop through the fileList by <tr ng-repeat="file in fileList"> to show the file information.
-
Each file have 3 auto property name,type and size. This 3 property of file you can access direcly.
now move to our controller and lets develop the setFile() function to set the fileList. As soon as we set the fileList with your selected image , Angular show the selected file infomation imediately. That's is the magic of Angular auto binding.
First we have to difine angular module. In Module.js File we did this. here is Module.js file
var app = angular.module('AgApp', [])
now use this module to develop our Angular controller for file upload. following code is a angular controler with SetFile() finction. The file ImageUploadMultipleController.js is as follows
app.controller('ImageUploadMultipleCtrl', function ($scope) {
$scope.fileList = [];
$scope.curFile;
$scope.ImageProperty = {
file:''
}
$scope.setFile = function (element) {
$scope.fileList = [];
var files = element.files;
for(var i=0;i<files.length;i++)
{
$scope.ImageProperty.file = files[i];
$scope.fileList.push($scope.ImageProperty);
$scope.ImageProperty = {};
$scope.$apply();
}
}
});
here ImageUploadMultipleCtrl is our controller name. The $scope variable fileList is an array of files that you selected. This end our step1, Run the project and slelect multiple file by press Ctrl key and you will find the following effect. Dont forget to add Module.js and imageUploadMultipleController.js at the end our page as shown in the above Html file.
Step 2: Develop Upload functionality With XMLHttpRequest
we will develop our upload function with XMLHttpRequest. XMLHttpRequest is an API that provides client functionality for transferring data between a client and a server. It provides an easy way to retrieve data from a URL without having to do a full page refresh. XMLHttpRequest was originally designed by Microsoft and adopted by Mozilla, Apple, and Google. For get more information XMLHttpRequest refer to
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
I chose XMLHttpRequest for the following reasons
- It provide us asynchronous upload of multiple files
- It provides tha way for tracking the progress of each files
Lets start developing our code. After completing Step1 we got all file in array named fileList. so loop through the fileList and get each file from the list with name,size and type and send all to another function to upload. so add two function at our controller at imageUploadMultipleController.js
- UploadFile() -- this function call when we click upload button.
- UploadFileIndividual(fileToUpload,name,type,size,index) -- responsible for upload indivudual file. it take 4 parameter: the file to upload,the file name,the file type ,file size and index
$scope.UploadFile=function()
{
for (var i = 0; i < $scope.fileList.length; i++)
{
$scope.UploadFileIndividual($scope.fileList[i].file,
$scope.fileList[i].file.name,
$scope.fileList[i].file.type,
$scope.fileList[i].file.size,
i);
}
}
$scope.UploadFileIndividual = function (fileToUpload,name,type,size,index)
{
var reqObj = new XMLHttpRequest();
reqObj.open("POST", "/FileUpload/UploadFiles", true);
reqObj.setRequestHeader("Content-Type", "multipart/form-data");
reqObj.setRequestHeader('X-File-Name', name);
reqObj.setRequestHeader('X-File-Type', type);
reqObj.setRequestHeader('X-File-Size', size);
reqObj.send(fileToUpload);
}
That's all . The commnet is well enough to describe the code. The result of this step can not visible with out develop ASP.net MVC controller to receive the object send by XMLHttpRequest.
Step 3: Develop ASP.net MVC Controller
Now we have to develop a ASP.net MVC controller to receive the file send from the XMLHttpRequest object
[HttpPost]
public virtual string UploadFiles(object obj)
{
var length = Request.ContentLength;
var bytes = new byte[length];
Request.InputStream.Read(bytes, 0, length);
var fileName = Request.Headers["X-File-Name"];
var fileSize = Request.Headers["X-File-Size"];
var fileType = Request.Headers["X-File-Type"];
var saveToFileLoc = "\\\\adcyngctg\\HRMS\\Images\\" + fileName;
// save the file.
var fileStream = new FileStream(saveToFileLoc, FileMode.Create, FileAccess.ReadWrite);
fileStream.Write(bytes, 0, length);
fileStream.Close();
return string.Format("{0} bytes uploaded", bytes.Length);
}
now if we run the project and click upload file button after selecting the image, we will find all the image uploaded successfully . But we do not get any message or progress. Let's move to next step where we develop the event handler for XMLHttpRequest object
Step 4: Traceing progress of individual file Upload
XMLHttpRequest object returns status of each event of each file. By writing a event handlar function we can access return status. We can use addEventListener method of XMLHttpRequest object to handle event . XMLHttpRequest raise following event at the different status of uploading
- progress : it raise during upload process and send us necessary information on progress status
- load : it raise after uploaded file saved at destination and server send back a response
- error : it raise imediately if any error occurs during upload
- abort : it raise imediately if user cancel the upload process
To handle the progress we have to define
reqObj.upload.addEventListener("progress", uploadProgress, false)
function uploadProgress(evt) {
if (evt.lengthComputable) {
var uploadProgressCount = Math.round(evt.loaded * 100 / evt.total);
document.getElementById('P' + index).innerHTML = uploadProgressCount;
// when upload complete then show a spiner icon till the server send back a response
if(uploadProgressCount==100)
{
document.getElementById('P' + index).innerHTML =
'<i class="fa fa-refresh fa-spin" style="color:maroon;"></i>';
}
}
}
To handle load complete we have to define
reqObj.addEventListener("load", uploadComplete, false)
function uploadComplete(evt) {
document.getElementById('P' + index).innerHTML = 'Saved';
$scope.NoOfFileSaved++;
$scope.$apply();
}
To handle error during upload we have to define
reqObj.addEventListener("error", uploadFailed, false)
function uploadFailed(evt) {
document.getElementById('P' + index).innerHTML = 'Upload Failed..';
}
To handle upload cancel we have to define
reqObj.addEventListener("abort", uploadCanceled, false)
function uploadCanceled(evt) {
document.getElementById('P' + index).innerHTML = 'Canceled....';
}
That's all. Our upload controller is now ready. The final controller look like this (imageUploadMultipleController.js)
$scope.UploadFileIndividual = function (fileToUpload,name,type,size,index)
{
var reqObj = new XMLHttpRequest();
reqObj.upload.addEventListener("progress", uploadProgress, false)
reqObj.addEventListener("load", uploadComplete, false)
reqObj.addEventListener("error", uploadFailed, false)
reqObj.addEventListener("abort", uploadCanceled, false)
reqObj.open("POST", "/FileUpload/UploadFiles", true);
reqObj.setRequestHeader("Content-Type", "multipart/form-data");
reqObj.setRequestHeader('X-File-Name', name);
reqObj.setRequestHeader('X-File-Type', type);
reqObj.setRequestHeader('X-File-Size', size);
reqObj.send(fileToUpload);
function uploadProgress(evt) {
if (evt.lengthComputable) {
var uploadProgressCount = Math.round(evt.loaded * 100 / evt.total);
document.getElementById('P' + index).innerHTML = uploadProgressCount;
if(uploadProgressCount==100)
{
document.getElementById('P' + index).innerHTML =
'<i class="fa fa-refresh fa-spin" style="color:maroon;"></i>';
}
}
}
function uploadComplete(evt) {
document.getElementById('P' + index).innerHTML = 'Saved';
$scope.NoOfFileSaved++;
$scope.$apply();
}
function uploadFailed(evt) {
document.getElementById('P' + index).innerHTML = 'Upload Failed..';
}
function uploadCanceled(evt) {
document.getElementById('P' + index).innerHTML = 'Canceled....';
}
}
Run the project and you will get the following picture.(Please Change the web.config as shown at Step5)
Step 5: Change the Maximum Upload length is Web.config
In order to uplaod large inage you have change the maximum upload length in Web.config.
In <system.web> section change the http in the following way
<httpRuntime targetFramework="4.5" maxRequestLength="1048576" />
Add/Update <system.webServer> section in the following way
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="1073741824" />
</requestFiltering>
remind the value maxRequestLength in <system.web> and maxAllowedContentLength in <system.webserver> must be same.
Final Word
It tesed it with different types of files. When you test it try to use to different computer. i mean try to select file from a network computer. so that you can find the best result of progress count. Or you can upload the project to a live server and try to upload file from your computer. If run it at you local computer and select file from same computer you may not see the progress count effect as uploading from local mechine to local mechine is very fast.