Saturday 10 March 2018

Upload and Download Functionality In AngularJS And WebApi By Converting The File Into Base64 Format

              In this article, I wanna share about upload and download of files to database in angularjs and WebAPI. In this for Uploading functionality , I am converting the selected file from File browse input control and adding (pushing) the files into a angularjs scope object of array  and in the submit button click event, converting the list of files from the scope object to base64 format and passing to webapi and saving in the database. And again in Download functionality , getting back the base64 format of file  from WebAPI to AngularJS http function as response into byte format and converts to the File Type (ex: docx, pdf, jpg … etc) and downloaded to the download folder.


Create a database Named File Data as follows :

USE [Employee]
GO

/****** Object:  Table [dbo].[FileData]    Script Date: 2/23/2018 10:31:24 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[FileData](
       [Id] [int] IDENTITY(1,1) NOT NULL,
       [FileName] [nvarchar](50) NULL,
       [FileDescription] [nvarchar](100) NULL,
       [File] [nvarchar](max) NULL,
       [FileType] [nvarchar](50) NULL,
 CONSTRAINT [PK_FileData] PRIMARY KEY CLUSTERED
(
       [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO


Now For uploading Files to Database Using AngularJS and WebApi

Create a project in a solution (this project is for html and js files)

And create a html file and write the below code :

index.html
<!DOCTYPE html>
<html ng-app="angularuploaddownloadfiles">
<head>
    <meta charset="utf-8" />
    <title>AngularJS And WebAPI - Upload And Download Files</title>
    <!--<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.min.js"></script>-->
    <!--If No Net Connection-->
    <!-- <link rel="stylesheet" href="" />-->
    <link href="AngularJS-Scripts/css/bootstrap.min.css" rel="stylesheet" />
    <script src="AngularJS-Scripts/js/angular.min.js"></script>
    <script src="index.js"></script>
    <script src="customdirectives.js"></script>
</head>
<body ng-controller="UploadDownloadFileController">
    <form name="formvalidation" novalidate>
        <div class="container">
            <div class="col-sm-8 col-sm-offset-2">

                <!-- Page Header -->
                <div class="page-header"><h1>AngularJS And WebAPI - Upload And Download Files</h1></div>
                <div class="form-group">
                    <div class="row">
                        <div class="col-md-4">
                            <label>Upload :</label>
                            <input type="file" class="form-control" id="file1" name="file" ng-files="getFileDetails($files)" />
                            <span style="color:red;font-size:smaller;" ng-show="UploadFileLimit">{{FilesLimitErrorMessage}}</span>
                        </div>
                        <div class="col-md-5">
                            <label>File Description :</label>
                            <input class="form-control" name="filedescription" ng-model="FileDescription" />
                        </div>
                        <div class="col-md-3">
                            <button name="add" class="btn btn-primary" ng-click="uploadFiles()">Add</button>
                            <button name="clear" class="btn btn-danger" ng-click="ClearData()">Clear</button>
                        </div>
                    </div>

                    <div class="col-md-12 text-center">
                        <div class="row">
                            <span style="color:red;font-size:smaller"> The total limit of files to be upload is 10 MB.</span>
                        </div>
                        <div class="row">
                            <span style="color:red;font-size:smaller;">{{SelectFilesErrorMessage}}</span>
                        </div>
                    </div>
                    <div class="row">
                        <div id="UploadFilesList">
                            <table class="table table-responsive">
                                <thead>
                                    <tr>
                                        <th>SNO</th>
                                        <th>File Name</th>
                                        <th>File Description</th>
                                        <th>Action</th>
                                        <th>&nbsp;</th>
                                    </tr>
                                </thead>
                                <tr ng-repeat="file in files">
                                    <td style="width:20%">{{$index + 1}}</td>
                                    <td class="truncate" style="width:30%">{{ file.name.substr(0, file.name.lastIndexOf('.')).trunc(40) }}</td>
                                    <td style="width:30%">{{ file.FileDescription }}</td>
                                    <td style="width:20%">
                                        <button class="btn btn-primary btn-sm" ng-click="DeleteFile($index)">Delete</button>
                                    </td>
                                </tr>
                            </table>

                        </div>
                    </div>
                    <div class="text-center">
                        <span ng-show="ShowLoader">
                            <img src="./images/loading.gif" class="img-loading" />
                        </span>
                        <span ng-show="ShowSuccessMessage" style="color:green;font-size:larger">{{SubmitFilesSucessMessage}}</span><br />
                        <span ng-show="ShowErrorFiles" style="color:red;font-size:larger">{{SubmitFilesErrorMessage}}</span><br />
                        <button class="btn btn-primary btn-sm" ng-click="SubmitFiles()">Submit</button>
                    </div>
                </div>
                <div class="form-group">
                    <div class="row">
                        <div id="UploadFilesList">
                            <table class="table table-responsive">
                                <thead>
                                    <tr>
                                        <th>SNO</th>
                                        <th>File Name</th>
                                        <th>File Description</th>
                                        <th>Action</th>
                                        <th>&nbsp;</th>
                                    </tr>
                                </thead>
                                <tr ng-repeat="file in GetFileData">
                                    <td>{{file.Id}}</td>
                                    <td>{{file.FileName}}</td>
                                    <td>{{ file.FileDescription }}</td>
                                    <td>
                                        <button ng-if="file.File!=null" class="btn btn-primary btn-sm" ng-click="DirectDownloadFile(file.Id)">Download From Base64</button>
                                        <p ng-if="file.File==null">No File Found</p>
                                    </td>
                                   
                                </tr>
                            </table>

                        </div>
                    </div>
                </div>
            </div>
        </div>
    </form>

</body>
</html>
Now create a js file and write the below code : that is containing with creation of angularjs module and controller
index.js
var app = angular.module('angularuploaddownloadfiles', []);
app.controller('UploadDownloadFileController', function ($scope, $window, $http) {
    var serviceBasePath = 'http://localhost:2653';

    $scope.DirectDownloadFile = function (fileId) {
        $http.get(serviceBasePath + '/api/Download/DirectDownloadFile?FileId=' + fileId, { responseType: 'arraybuffer' }).then(function (response) {

            var filename = response.headers()['x-filename'] || ("Document_" + new Date() + ".pdf");
            var contentType = response.headers()['content-type'];

            var linkElement = document.createElement('a');
            try {
                var blob = new Blob([response.data], { type: contentType });
                var url = window.URL.createObjectURL(blob);

                linkElement.setAttribute('href', url);
                linkElement.setAttribute("download", filename);

                var clickEvent = new MouseEvent("click", {
                    "view": window,
                    "bubbles": true,
                    "cancelable": false
                });
                linkElement.dispatchEvent(clickEvent);
            } catch (ex) {
                console.log(ex);
            }
        }, function (error) {

        })
      
    }
$scope.files = [];
    $scope.encodedbase64_file = [];
    var fileSize = 0;
    var fileSizeAdd = 0;
    var FixedFileSize = 10485760;
    ////TO GET THE FILE INFORMATION.
    $scope.getFileDetails = function ($files) {
       
        var allowfiles = 0;
        fileSizeAdd = ($scope.files && $scope.files.length) ? fileSizeAdd : 0;
        fileSize = ($scope.files && $scope.files.length) ? fileSize : 0;
        $scope.$apply(function () {
            $scope.UploadFileLimit = false;
            $scope.FilesLimitErrorMessage = "";
        })

        $scope.SelectFilesErrorMessage = "";
        var allowedFileFormats = ["jpg", "jpeg", "png", "pdf", "doc", "docx", "tiff", "tif", "bmp"];
       
        if ($files && !$scope.SelectFilesErrorMessage) {

            for (var i = 0; i < $files.length; i++) {
                if (fileSizeAdd == fileSize) {
                    fileSize = parseFloat(fileSize) + parseFloat($files[i].size);
                }
                else {
                    if ($files[i].size < FixedFileSize) fileSize = fileSizeAdd;
                    else fileSize = parseFloat(fileSize) + parseFloat($files[i].size);
                }
                if (allowedFileFormats.indexOf($files[i].name.split(".").pop().toLowerCase()) > -1) {
                    $scope.FilesLimitErrorMessage = "";
                }
                else {
                    $scope.FilesLimitErrorMessage = "Only  jpg, jpeg, png, pdf, doc, docx, tiff, tif, bmp files are allowed!";
                    break;
                }
            }
            if (!$scope.FilesLimitErrorMessage) {
                if (fileSize <= FixedFileSize) { 
                    $scope.UploadFileLimit = false;
                    $scope.FilesLimitErrorMessage = "";
                    $scope.filesList = [];
                    $scope.$apply(function () {

                        //// STORE THE FILE OBJECT IN AN ARRAY.
                        for (var i = 0; i < $files.length; i++) {
                            $scope.filesList.push($files[i])

                            setupReader($files[i]);

                        }
                        function setupReader(file) {
                            var name = file.name;
                            var reader = new FileReader();
                            reader.onload = function (e) {
                                // get file content 
                                $scope.encodedbase64_file.push(e.target.result);
                            }
                            reader.readAsDataURL(file);
                        }
                        $scope.disbleUploadFile = true;
                    });
                    if ($scope.filesList)
                        $scope.btnAddDisable = false;
                }
                else {
                    //  angular.element("input[type='file']").val(null);
                }
            }
            else {
                $scope.FilesLimitErrorMessage = "Only  jpg, jpeg, png, pdf, doc, docx, tiff, tif, bmp files are allowed!";
            }
        }
    };
//// To add the selected files to the grid table
    $scope.uploadFiles = function () {
        if ($scope.FileDescription) {
            $scope.SelectFilesErrorMessage = "";

            if ($scope.filesList && $scope.filesList.length && !$scope.SelectFilesErrorMessage) {
                $scope.ValidationMessage = "";
                var files = $scope.filesList;
                fileSizeAdd = fileSize;
                //  $scope.files = files;
                if (files && files.length > 0) {
                    var data = new FormData();
                    for (i = 0; i < files.length; i++) {
                        data.append("file" + i, files[i]);
                        files[i].FileDescription = $scope.FileDescription;
                        $scope.files.push(files[i]);
                        $scope.filesList = [];
                    }
                }
                if ($scope.files && $scope.files.length > 0) {
                    $scope.btnAddDisable = true;
                    $scope.disbleUploadFile = true;
                }
                else {
                    $scope.btnAddDisable = false;
                    $scope.disbleUploadFile = false;
                    $scope.UploadFileLimit = true;
                    if (fileSize > FixedFileSize) 
                        $scope.FilesLimitErrorMessage = $scope.FilesLimitErrorMessage ? $scope.FilesLimitErrorMessage : "Files Limit Exceeded 10 MB";  //// 10MB=10485760  Bytes (binary)
                    // angular.element("input[type='file']").val(null);
                }
            }
            else {
                if (fileSize > FixedFileSize) {
                    $scope.UploadFileLimit = true;
                    $scope.FilesLimitErrorMessage = $scope.FilesLimitErrorMessage ? $scope.FilesLimitErrorMessage : "Files Limit Exceeded 10 MB";

                }
                else {
                    if ($scope.FilesLimitErrorMessage) {
                        $scope.UploadFileLimit = true;
                    }
                    else {
                        $scope.UploadFileLimit = false;
                        $scope.SelectFilesErrorMessage = "Please Choose File To Upload";
                    }
                }
            }
        }
        else $scope.SelectFilesErrorMessage = $scope.SelectFilesErrorMessage ? $scope.SelectFilesErrorMessage : "Please Enter File Name";
    };
    /// To Delete the files in the grid table by Id
    $scope.DeleteFile = function (id) {
        var index = -1;
        var comArr = eval($scope.files);
        for (var i = 0; i < comArr.length; i++) {
            if (i === id) {
                index = i;
                break;
            }

        }
        if (index === -1) {
            alert("Something gone wrong");
        }
        fileSize = parseFloat(fileSize) - parseFloat($scope.files[index].size);
        if (fileSize <= FixedFileSize) {
            $scope.UploadFileLimit = false;
            $scope.FilesLimitErrorMessage = "";
        }
        //  var index = $scope.files.indexOf(name);
        $scope.files.splice(index, 1);
        if ($scope.files.length <= 0) {
            $scope.btnAddDisable = false;
            $scope.disbleUploadFile = false;
            // angular.element("input[type='file']").val(null);
        }
        angular.element("input[type='file']").val();
    }

    //// To Submit files to webapi
    $scope.SubmitFiles = function () {
        $scope.UploadFilesList = [];
        $scope.UploadFileData = [];
        $scope.FileTypeData = [];
        $scope.ShowErrorFiles = false;
        $scope.SubmitFilesErrorMessage = "";
        for (var i = 0; i < $scope.files.length; i++) {
            $scope.UploadFileData.push($scope.encodedbase64_file[i].substring($scope.encodedbase64_file[i].indexOf("base64,") + 7));
            $scope.FileTypeData.push($scope.encodedbase64_file[i].substring(5, $scope.encodedbase64_file[i].indexOf(";")));
            var FileDetails = {
                File: $scope.UploadFileData[i],
                FileName: $scope.files[i].name.substr(0, $scope.files[i].name.lastIndexOf('.')),
                FileDescription: $scope.FileDescription,
                FileType: $scope.FileTypeData[i]
            }
       
            $scope.UploadFilesList.push(FileDetails);
        }
      
        if ($scope.UploadFilesList.length && !$scope.SelectFilesErrorMessage) {
            $scope.ShowLoader = true;
            $http.put(serviceBasePath + '/api/Upload/UploadFile', $scope.UploadFilesList).then(function (response) {
                $scope.ShowLoader = false;
                $scope.ShowSuccessMessage = true;
                $scope.SubmitFilesSucessMessage = response.data;
                $window.location.reload();

            }, function (error) {
                var errorMessages = [];
                if (error.data && error.data.ModelState) {
                    for (var key in error.data.ModelState) {
                        for (var i = 0; i < error.data.ModelState[key].length; i++) {
                            errorMessages.push(error.data.ModelState[key][i]);
                        }
                    }
                    // alert(errorMessages[0]);
                    $scope.ShowLoader = false;
                    $scope.ShowErrorFiles = true;
                    $scope.SubmitFilesErrorMessage = errorMessages[0];
                }
                else {
                    $scope.ShowLoader = false;
                    error ? ((error.data && error.data.Message) ? $scope.OrderListErrorMessage = error.data.Message : $scope.OrderListErrorMessage = "The Request Is Invalid") : $scope.OrderListErrorMessage = "Invalid Request";
                }
            })
        }
        else {
            $scope.SelectFilesErrorMessage = $scope.SelectFilesErrorMessage ? $scope.SelectFilesErrorMessage : "Please Select A File";
        }
    }
//// To Clear The popup controls when Open the popup
    $scope.ClearData = function (item) {
        angular.element("input[type='file']").val(null);
        $scope.CaseId = item.SugarCRMCaseId;
        $scope.files = [];
 $scope.FileDescription = "";
        $scope.disbleUploadFile = true;
        $scope.FilesLimitErrorMessage = "";
        $scope.btnAddDisable = false;
        $scope.selectedCard = 0;
        $scope.ShowSuccessMessage = false;
        $scope.SubmitFilesSucessMessage = "";
        $scope.SelectFilesErrorMessage = "";
    }
    var InIt = function () {
        $http.get(serviceBasePath + '/api/Upload/GetAllData').then(function (response) {
            $scope.GetFileData = response.data;

        }, function (error) {

        })
    }
    InIt();
});
Again create another js file and code with custom directive to upload file and a prototype to truncate the file name in html page
customdirectives.js
app.directive('ngFiles', ['$parse', function ($parse) {
    function fn_link(scope, element, attrs) {
        var onChange = $parse(attrs.ngFiles);
        element.on('change', function (event) {
            onChange(scope, { $files: event.target.files });
        });
    };

    return {
        link: fn_link
    }
}]);

/**
 * extends string prototype object to get a string with a number of characters from a string.
 *
 * @type {Function|*}
 */
//// to truncate the given string
String.prototype.trunc = String.prototype.trunc ||
function (n) {

    // this will return a substring and
    // if its larger than 'n' then truncate and append '...' to the string and return it.
    // if its less than 'n' then return the 'string'
    return this.length > n ? this.substr(0, n - 1) + '...' : this.toString();
};

And Now We have to add another project to the same solution for  WebAPI
Now create a controllers named UploadController and DownloadController to receive the request from Angularjs project and passes to WebAPI repository Where Repository contains the business logic that we write the code for uploading files to database and downloading files from database :
UploadController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using WebApi_UploadAndDownloadFiles.Models;
using WebApi_UploadAndDownloadFiles.Repository;

namespace WebApi_UploadAndDownloadFiles.Controllers
{
    public class UploadController : ApiController
    {

        [HttpPut]
        [Route("api/Upload/UploadFile")]
        public async Task<IHttpActionResult> UploadFileToDB(List<UploadFilesList> uploadfiles)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
            try
            {
                UploadRepository uploadRepo = new UploadRepository();
                var result = await uploadRepo.UploadFileToDB(uploadfiles);
                return Ok(result);
            }
            catch (Exception ex)
            {
                return null;
            }
          
            }
       
        [HttpGet]
        [Route("api/Upload/GetAllData")]
    public async Task<List<FileData>> GetAllData()
    {
        try
        {
            UploadRepository uploadRepo = new UploadRepository();
            var result = await uploadRepo.GetData();
            return result;
        }
        catch (Exception ex)
        {
            return null;
        }
    }
}
}

DownloadController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using WebApi_UploadAndDownloadFiles.Repository;

namespace WebApi_UploadAndDownloadFiles.Controllers
{
    public class DownloadController : ApiController
    {
        //File Download By Converting Base64 Format
        [HttpGet]
        [Route("api/Download/DirectDownloadFile")]
        public HttpResponseMessage DirectDownload(int FileId)
        {
            try
            {
                DownloadRepository downloadRepo = new DownloadRepository();
                HttpResponseMessage result = downloadRepo.DirectDownloadFromBase64(FileId);
                return result;
            }
            catch (Exception ex)
            {
                return this.Request.CreateResponse(HttpStatusCode.InternalServerError, ex);
            }
        }

    
    }
}

And Now create a folder named Repository and add the cs files named UploadRepository and DownloadRepository and write business logic as below :
UploadRepository.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using WebApi_UploadAndDownloadFiles.Models;

namespace WebApi_UploadAndDownloadFiles.Repository
{
    public class UploadRepository
    {
        public async Task<List<FileData>> GetData()
        {
            using (FileEntities db = new FileEntities())
            {
                var fileData = db.FileDatas.Select(x => x).ToList();
                return await Task.FromResult(fileData);
            }
        }
        public async Task<string> UploadFileToDB(List<UploadFilesList> uploadfiles)
        {
            using (FileEntities db = new FileEntities())
            {
                for (int i = 0; i < uploadfiles.Count(); i++)
                {
                    FileData fileData = new FileData()
                    {
                        File = uploadfiles[i].File,
                        FileDescription = uploadfiles[i].FileDescription,
                        FileName = uploadfiles[i].FileName,
                        FileType= uploadfiles[i].FileType
                    };
                    db.FileDatas.Add(fileData);
                }
                db.SaveChanges();
            }
            return await Task.FromResult("Saved Successfully");
        }
    }
}
DownloadRepository.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web;

namespace WebApi_UploadAndDownloadFiles.Repository
{
    public class DownloadRepository
    {
        public HttpResponseMessage DirectDownloadFromBase64(int FileId)
        {
            using (FileEntities db = new FileEntities())
            {
                var fileData = db.FileDatas.Where(x => x.Id == FileId).FirstOrDefault();

                string fileName = fileData.FileName;// + ".pdf";//"Document" + DateTime.Now.ToString("_MMMdd_yyyy_HHmmss") + ".pdf";
                string base64formatstring = fileData.File;
                if (!string.IsNullOrEmpty(base64formatstring))
                {
                    byte[] bytes = Convert.FromBase64String(base64formatstring);

                    HttpResponseMessage httpResponseMessage = new HttpResponseMessage();
                    httpResponseMessage.Content = new ByteArrayContent(bytes.ToArray());
                    httpResponseMessage.Content.Headers.Add("x-filename", fileName);
                    httpResponseMessage.Content.Headers.Add("Access-Control-Expose-Headers", "x-filename"); //Basically Adding Access-Control-Expose-Headers helps to expose the custom headers to client side.
                    // httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
                    httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue(fileData.FileType);
                    httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
                    httpResponseMessage.Content.Headers.ContentDisposition.FileName = fileName;
                    httpResponseMessage.StatusCode = HttpStatusCode.OK;
                    return httpResponseMessage;
                }
            }
            return null;
        }

      
    }
}

As we have two projects in one solution, we have to start the two projects while running, for that right click on the solution file in Solution Explorer and Click on the 'Properties' and in the displayed window in 'Common Properties'  select 'Startup Project' and in the displayed right side select the radio button 'Multiple startup projects'  and select both project Action as 'Start'.

While Client request hits the webapi, then we will get CORS (Cross Origin Request Sharing) error, as below : 

Failed to load http://localhost:2653/api/Upload/GetAllData: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:2647' is therefore not allowed access.

So, to prevent this we have to add the below code in Register() method in WebApiConfig static class :

 var cors = new EnableCorsAttribute("*", "*", "*");

 config.EnableCors(cors);

i.e.,

public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            // Configure Web API to use only bearer token authentication.
            config.SuppressDefaultHostAuthentication();
            config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
            //To Convert To Json Format From Text Format
            config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
            // To prevent the CORS error, if request comes from different host
            var cors = new EnableCorsAttribute("*", "*", "*");
            config.EnableCors(cors);
            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }

    }

Output:-



If trying to add other format of files than the mentioned format as below, It shows error message:
  var allowedFileFormats = ["jpg", "jpeg", "png", "pdf", "doc", "docx", "tiff", "tif", "bmp"];


Now choose the file to upload as below :



Now Click on  Add button , then the File data is added to the list as below :


Now choose another file and add as below :





Now Click on submit button, then the data is added to the below grid as below :


 Now click on download button, it will download the file to download folder as below :



Download Code From GitHub

No comments:

Post a Comment

Note: only a member of this blog may post a comment.