Angular 4 Multiple File Upload using ASP.NET Web API and C#

In this article,  you will learn by creating a complete application for Angular 4 Multiple File Upload using ASP.NET Web API and C#

I will describe how to upload multiple files using Angular 4, ASP.NET Web API and C# by extending the solution developed in the previous article – Angular 4 QR Code Generator using ASP.NET Web API and C#. I will add new component for uploading files to the previous article’s source code.

Technologies Used : Angular 4, Web API, C#, HTML 5, Bootstrap

Tools needed : Visual Studio 2017.

Source Code : Complete source code can be found on GitHub repository :
https://github.com/sudipta-chaudhari/angular4_FileUpload_AspNetWebAPI

Application Architecture :
Angular4_Multiple_File_Upload_Architecture

Using the source code from my previous article Generate QR Code using Angular 4 and ASP.NET Web API, proceed as mentioned in the below steps.

Step (1) : Create Web API controller

Add a new Web API controller and name it UploadController. Create a POST method which will upload files and then return a string which will have the information on how many files were uploaded successfully and their respective file names.

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Web;
using System.Web.Http;

namespace Angular4WebApi.Controllers
{
    public class UploadController : ApiController
    {
        [HttpPost]
        public IHttpActionResult UploadFiles()
        {
            int i = 0;
            int cntSuccess = 0;
            var uploadedFileNames = new List<string>();
            string result = string.Empty;

            HttpResponseMessage response = new HttpResponseMessage();

            var httpRequest = HttpContext.Current.Request;
            if (httpRequest.Files.Count > 0)
            {
                foreach (string file in httpRequest.Files)
                {
                    var postedFile = httpRequest.Files[i];
                    var filePath = HttpContext.Current.Server.MapPath("~/UploadedFiles/" + postedFile.FileName);
                    try
                    {
                        postedFile.SaveAs(filePath);
                        uploadedFileNames.Add(httpRequest.Files[i].FileName);
                        cntSuccess++;
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }

                    i++;
                }
            }

            result = cntSuccess.ToString() + " files uploaded succesfully.<br/>";

            result += "<ul>";

            foreach (var f in uploadedFileNames)
            {
                result += "<li>" + f + "</li>";
            }

            result += "</ul>";

            return Json(result);
        }
    }
}

Non .NET/Microsoft audience can create any REST api using Node.js, PHP etc to upload files.

The above Web API’s POST method UploadFiles() gets the files from the HTTP Request object and used a for-each loop to save the files to ‘Upload’ directory on the web server’s root directory’s ‘UploadedFiles’ folder.

Step (2) : Create View for Upload Component

Under app folder at the root of the application, create a new folder upload and add two files – upload.component.html and upload.component.ts.

In the upload.component.html file, add a HTML 5 file upload element and set attribute ‘multiple’ to upload multiple files at once. Also, set attribute accept=".pdf" to accept PDF files only to upload. accept attribute doesn’t work on IE 11, Edge etc browsers, hence a separate check in Typescript and server side code of Web API is also needed.

Also, in this html file, I have used a custom CSS class spinner to  display a loading spinner which will be controlled by TypeScript for the Upload component to show the spinner while the file upload is in progress and hide the spinner when the file upload completes.

Complete code for upload.component.html  is shown below

<style type="text/css">
    #spinner {
        background-color: rgba(49, 37, 37, 0.2);
        border-radius: 6px;
        top: 0;
        left: 0;
        height: 100%;
        width: 100%;
        position: fixed;
        content: " ";
        text-align: center;
        z-index: 9999;
        /*Center Div*/
        /* Safari, Opera, and Chrome */
        display: -webkit-box;
        -webkit-box-pack: center;
        -webkit-box-align: center;
        /* Firefox */
        display: -moz-box;
        -moz-box-pack: center;
        -moz-box-align: center;
        /* Internet Explorer 10 */
        display: -ms-flexbox;
        -ms-flex-pack: center;
        -ms-flex-align: center;
    }
</style>

<h2>Upload Files</h2>

<div *ngIf="isLoadingData" style="text-align:center" id="spinner">
    <img src="./Content/page-loader.gif" />
</div>

<input #fileUpload type="file" (change)="fileChangeEvent($event)" placeholder="Upload file..." multiple accept=".pdf" /><br />

<button type="button" (click)="upload()">Upload</button>
<button type="button" (click)="cancelUpload()">Cancel</button>

<div *ngIf="filesToUpload?.length > 0"><br /><p>{{filesToUpload?.length}} files selected : </p></div>

<ul>
    <li *ngFor="let fileName of selectedFileNames; let i = index">
        {{fileName}}
    </li>
</ul>

<div [innerHtml]="uploadResult"></div>

<div style="color:red">
    {{errorMessage}}
</div>

The HTML 5 file upload control’s ‘change’ event has function ‘fileChangeEvent’ in which the event of the file upload control is passed. An Upload button is defined and on click of this button, the ‘upload’ function is triggered from TypeScript code. The ‘Cancel Upload’ button clears the upload field and resets any variables in TypeScript code. I will be omitting the code for the ‘Cancel Upload’ button and leave it to the reader.

Step (3) : Create TypeScript for the Upload Component

TypeScript code for the Upload component upload.component.ts is shown below.

import { Component, ViewChild } from '@angular/core';
import { Http, RequestOptions, Headers, Response } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import { Router } from '@angular/router';
import { UploadedFile } from '../model/uploadedfile';

@Component({
    templateUrl: './app/upload/upload.component.html'
})

export class UploadComponent {
    errorMessage: string;
    filesToUpload: Array<File>;
    selectedFileNames: string[] = [];
    public isLoadingData: Boolean = false;
    @ViewChild('fileUpload') fileUploadVar: any;
    uploadResult: any;
    res: Array<string>;

    constructor(private http: Http, private router: Router) {
        this.errorMessage = "";
        this.filesToUpload = [];
        this.selectedFileNames = [];
        this.uploadResult = "";
    }
    
    fileChangeEvent(fileInput: any)
    {
        //Clear Uploaded Files result message
        this.uploadResult = "";
        this.filesToUpload = <Array<File>>fileInput.target.files;

        for (let i = 0; i < this.filesToUpload.length; i++)
        {
            this.selectedFileNames.push(this.filesToUpload[i].name);
        }
    }

    cancelUpload()
    {
        this.filesToUpload = [];
        this.fileUploadVar.nativeElement.value = "";
        this.selectedFileNames = [];
        this.uploadResult = "";
        this.errorMessage = "";
    }

    upload()
    {
        if (this.filesToUpload.length == 0)
        {
            alert('Please select at least 1 PDF files to upload!');
        }
        else if (this.filesToUpload.length > 3) {
            alert('Please select a maximum of 3 PDF files to upload!');
        }
        else
        {
            if (this.validatePDFSelectedOnly(this.selectedFileNames))
            {
                this.uploadFiles();
            }
        }
    }

    validatePDFSelectedOnly(filesSelected: string[])
    {
        for (var i = 0; i < filesSelected.length; i++)
        {
            if (filesSelected[i].slice(-3).toUpperCase() != "PDF")
            {
                alert('Please selecte PDF files only!');
                return false;
            }
            else {
                return true;
            }
        }
    }

    uploadFiles()
    {
        this.uploadResult = "";

        if (this.filesToUpload.length > 0)
        {
            this.isLoadingData = true;

            let formData: FormData = new FormData();

            for (var i = 0; i < this.filesToUpload.length; i++)
            {
                formData.append('uploadedFiles', this.filesToUpload[i], this.filesToUpload[i].name);
            }

            let apiUrl = "/api/Upload/UploadFiles";

            this.http.post(apiUrl, formData)
                .map((res: Response) => res.json())
                .subscribe
                (
                    data => {
                        this.uploadResult = data;
                        this.errorMessage = "";
                    },
                    err => {
                        console.error(err);
                        this.errorMessage = err;
                        this.isLoadingData = false;
                    },
                    () => {
                        this.isLoadingData = false,
                            this.fileUploadVar.nativeElement.value = "";
                        this.selectedFileNames = [];
                        this.filesToUpload = [];
                    }
                );
        }
    }
}

filesToUpload is an Array of Files and fileUploadVar is of type ViewChild which is used to access the HTML 5 fileUpload control from the HTML view in TypeScript code.

upload() function checks if no file is selected, alerts the user to do the same. Then the function checks if more than 3 files are selected for upload, alerts the user to select a maximum of 3 files. If the above two checks are valid, next the upload function check if all the files selected for upload are PDF files using the validatePDFSelectedOnly(filesSelected: string[]) function, then proceeds to call the uploadFiles() function to start uploading the files, else alerts the user that some of the files selected are not PDF files. This check is needed for browser like IE11, Edge etc. which don’t support the HTML 5 file upload attributes like ‘accept’.

isLoadingData variable is used to display a loading spinner by setting it to true when the upload function starts executing and is set to false in the Observable after the uploading files is completed.

Angular HTTP module is used to make a HTTP POST call the to Web API ‘/api/Upload/UploadFiles’ which is a POST api.

Also note the formData variable which is defined as a type of FormData. This variable holds all the uploaded files and this is used to pass the data to the POST Web API as formData.

Step (4) : Add Navigation link for Upload View

In the app.router.ts file, define create a route path for the upload component. Complete code for the file is shown below.

import { ModuleWithProviders } from '@angular/core';
import { Routes, RouterModule} from '@angular/router';
import { HomeComponent } from './home/home.component';
import { QRCodeComponent } from './qrcode/qrcode.component';
import { UploadComponent } from './upload/upload.component';

export const router: Routes = [
    { path: '', redirectTo: 'home', pathMatch: 'full' },
    { path: 'home', component: HomeComponent },
    { path: 'qrcode', component: QRCodeComponent },
    { path: 'upload', component: UploadComponent }
];

export const routes: ModuleWithProviders = RouterModule.forRoot(router);

Step (5) : Run the application

On running the application and navigating to the Upload route, the screen looks as shown in screenshot below.

angular4_file_upload_pg1

On selecting some PDF files for upload, the screen looks as in the screenshot below.

angular4_file_upload_pg2

On clicking the upload button and while upload is in progress, the busy indicator is displayed as shown in screenshot below.

angular4_file_upload_pg3

After the files have been uploaded successfully, the screen looks like as shown in screenshot below.

angular4_file_upload_pg4

Navigating to the “UploadedFiles” directotry, you can see the actual files uploaded as shown below.

angular4_file_upload_pg5

Hope you followed the article. If you have any comments, questions or suggestions, leave a message and I will try to respond at my earliest.

One thought on “Angular 4 Multiple File Upload using ASP.NET Web API and C#

Leave a comment