Click here to Skip to main content
15,508,577 members
Articles / Web Development / ASP.NET
Tip/Trick
Posted 26 Apr 2015

Stats

66.1K views
26 bookmarked

SFTP File Upload Using ASP.NET Web API and AngularJS

Rate me:
Please Sign up or sign in to vote.
4.96/5 (12 votes)
26 Apr 2015CPOL1 min read
Uplaod files to SFTP using ASP.NET Web API and AngularJS upload plugin by nervgh with Bootstrap 3.0

Introduction

Angular-file-upload directive by nervgh is an awesome lightweight AngularJS directive which handles file upload for you and lets you upload files asynchronously to the server. This post will give you basic understanding on how to upload files by using this directive together with .NET WebAPI service on SFTP server. For the purpose of this tutorial, I'll keep everything as simple as possible since the focus here is on connecting AngularJS and async upload with a .NET WebAPI service and not on additional functionality which can be built afterwards around angular-file-upload.

Background

From the unix man page: "sftp is an interactive file transfer program, similar to ftp, which performs all operations over an encrypted ssh transport".

Why is SFTP better than FTP?

In FTP, all data is passed back and forth between the client and server without the use of encryption. This makes it possible for an evesdropper to listen in and retrieve your confidential information including login details. With SFTP, all the data is encrypted before it is sent across the network.

Using the Code

HTML Code

HTML
<html id="ng-app" ng-app="app">
<head>
    <title>Simple example</title>
    <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/
     bootstrap.min.css" />
    <script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
    <script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
    <script src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
    <script src="~/Scripts/angular-file-upload.js"></script>
    <script src="~/Scripts/controllers.js"></script>
    <style>
        .my-drop-zone {
            border: dotted 3px lightgray;
        }

        .nv-file-over {
            border: dotted 3px red;
        }
        .another-file-over-class {
            border: dotted 3px green;
        }

        html, body {
            height: 100%;
        }
    </style>
</head>
<body ng-controller="AppController" nv-file-drop="" uploader="uploader"
filters="queueLimit, customFilter">
    <div class="container">
        <div class="row">
            <div class="col-md-3">
                <h3>Select files</h3>
                <div ng-show="uploader.isHTML5">
                    <div nv-file-drop="" uploader="uploader">
                        <div nv-file-over="" uploader="uploader"
                            over-class="another-file-over-class" class="well my-drop-zone">
                            Another drop zone with its own settings
                        </div>
                    </div>
                </div>
                Single
                <input type="file" nv-file-select="" uploader="uploader" />
            </div>
            <div class="col-md-9" style="margin-bottom: 40px">
                <h3>Upload queue</h3>
                <p>Queue length: {{ uploader.queue.length }}</p>
                <table class="table">
                    <thead>
                        <tr>
                            <th width="50%">Name</th>
                            <th ng-show="uploader.isHTML5">Size</th>
                            <th ng-show="uploader.isHTML5">Progress</th>
                            <th>Status</th>
                            <th>Actions</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr ng-repeat="item in uploader.queue">
                            <td><strong>{{ item.file.name }}</strong></td>
                            <td ng-show="uploader.isHTML5" nowrap>
                            {{ item.file.size/1024/1024|number:2 }} MB</td>
                            <td ng-show="uploader.isHTML5">
                                <div class="progress" style="margin-bottom: 0;">
                                    <div class="progress-bar" role="progressbar"
                                     ng-style="{ 'width': item.progress + '%' }"></div>
                                </div>
                            </td>
                            <td class="text-center">
                                <span ng-show="item.isSuccess"><i class="glyphicon glyphicon-ok">
                                </i></span>
                                <span ng-show="item.isCancel"><i class="glyphicon
                                glyphicon-ban-circle"></i></span>
                              <span ng-show="item.isError"><i class="glyphicon glyphicon-remove">
                                </i></span>
                            </td>
                            <td nowrap>
                                <button type="button" class="btn btn-success btn-xs"
                                 ng-click="item.upload()" ng-disabled="item.isReady ||
                                 item.isUploading || item.isSuccess">
                                    <span class="glyphicon glyphicon-upload"></span> Upload
                                </button>
                                <button type="button" class="btn btn-warning btn-xs"
                                 ng-click="item.cancel()" ng-disabled="!item.isUploading">
                                    <span class="glyphicon glyphicon-ban-circle"></span> Cancel
                                </button>
                                <button type="button" class="btn btn-danger btn-xs"
                                 ng-click="item.remove()">
                                    <span class="glyphicon glyphicon-trash"></span> Remove
                                </button>
                            </td>
                        </tr>
                    </tbody>
                </table>
                <div>
                    <div>
                        Queue progress:
                        <div class="progress" style="">
                            <div class="progress-bar" role="progressbar"
                             ng-style="{ 'width': uploader.progress + '%' }"></div>
                        </div>
                    </div>
                    <button type="button" class="btn btn-success btn-s"
                     ng-click="uploader.uploadAll()"
                     ng-disabled="!uploader.getNotUploadedItems().length">
                        <span class="glyphicon glyphicon-upload"></span> Upload all
                    </button>
                    <button type="button" class="btn btn-warning btn-s"
                     ng-click="uploader.cancelAll()" ng-disabled="!uploader.isUploading">
                        <span class="glyphicon glyphicon-ban-circle"></span> Cancel all
                    </button>
                    <button type="button" class="btn btn-danger btn-s"
                    ng-click="uploader.clearQueue()" ng-disabled="!uploader.queue.length">
                        <span class="glyphicon glyphicon-trash"></span> Remove all
                    </button>
                </div>
            </div>
        </div>
    </div>
</body>

AngularJS Controller Code

JavaScript
'use strict';

angular
.module('app', ['angularFileUpload'])
.controller('AppController', ['$scope', 'FileUploader', function($scope, FileUploader) {
    // Uploader Plugin Code
 
    var uploader = $scope.uploader = new FileUploader({
        url: window.location.protocol + '//' + window.location.host +
             window.location.pathname + '/api/Upload/UploadFile'
    });

    // FILTERS

    uploader.filters.push({
        name: 'extensionFilter',
        fn: function (item, options) {
            var filename = item.name;
            var extension = filename.substring(filename.lastIndexOf('.') + 1).toLowerCase();
            if (extension == "pdf" || extension == "doc" || extension == "docx" ||
                extension == "rtf")
                return true;
            else {
                alert('Invalid file format. Please select a file with pdf/doc/docs or
                rtf format and try again.');
                return false;
            }
        }
    });

    uploader.filters.push({
        name: 'sizeFilter',
        fn: function (item, options) {
            var fileSize = item.size;
            fileSize = parseInt(fileSize) / (1024 * 1024);
            if (fileSize <= 5)
                return true;
            else {
                alert('Selected file exceeds the 5MB file size limit.
                       Please choose a new file and try again.');
                return false;
            }
        }
    });

    uploader.filters.push({
        name: 'itemResetFilter',
        fn: function (item, options) {
            if (this.queue.length < 5)
                return true;
            else {
                alert('You have exceeded the limit of uploading files.');
                return false;
            }
        }
    });
 
    // CALLBACKS
 
    uploader.onWhenAddingFileFailed = function (item, filter, options) {
        console.info('onWhenAddingFileFailed', item, filter, options);
    };
    uploader.onAfterAddingFile = function (fileItem) {
        alert('Files ready for upload.');
    };

    uploader.onSuccessItem = function (fileItem, response, status, headers) {
        $scope.uploader.queue = [];
        $scope.uploader.progress = 0;
        alert('Selected file has been uploaded successfully.');
    };
    uploader.onErrorItem = function (fileItem, response, status, headers) {
        alert('We were unable to upload your file. Please try again.');
    };
    uploader.onCancelItem = function (fileItem, response, status, headers) {
        alert('File uploading has been cancelled.');
    };

    uploader.onAfterAddingAll = function(addedFileItems) {
        console.info('onAfterAddingAll', addedFileItems);
    };
    uploader.onBeforeUploadItem = function(item) {
        console.info('onBeforeUploadItem', item);
    };
    uploader.onProgressItem = function(fileItem, progress) {
        console.info('onProgressItem', fileItem, progress);
    };
    uploader.onProgressAll = function(progress) {
        console.info('onProgressAll', progress);
    };
        
    uploader.onCompleteItem = function(fileItem, response, status, headers) {
        console.info('onCompleteItem', fileItem, response, status, headers);
    };
    uploader.onCompleteAll = function() {
        console.info('onCompleteAll');
    };

    console.info('uploader', uploader);
}]);

Web API Controller

C#
[System.Web.Http.HttpPost]
public void UploadFile()
{
    if (HttpContext.Current.Request.Files.AllKeys.Any())
    {
        var httpPostedFile = HttpContext.Current.Request.Files["file"];
  bool folderExists = Directory.Exists(HttpContext.Current.Server.MapPath("~/UploadedDocuments"));
        if (!folderExists)
            Directory.CreateDirectory(HttpContext.Current.Server.MapPath("~/UploadedDocuments"));
        var fileSavePath = Path.Combine(HttpContext.Current.Server.MapPath("~/UploadedDocuments"),
                                        httpPostedFile.FileName);
        httpPostedFile.SaveAs(fileSavePath);

        if (File.Exists(fileSavePath))
        {
            //AppConfig is static class used as accessor for SFTP configurations from web.config
            using (SftpClient sftpClient = new SftpClient(AppConfig.SftpServerIp,
                                                         Convert.ToInt32(AppConfig.SftpServerPort),
                                                         AppConfig.SftpServerUserName,
                                                         AppConfig.SftpServerPassword))
            {
                sftpClient.Connect();
                if (!sftpClient.Exists(AppConfig.SftpPath + "UserID"))
                {
                    sftpClient.CreateDirectory(AppConfig.SftpPath + "UserID");
                }

                Stream fin = File.OpenRead(fileSavePath);
                sftpClient.UploadFile(fin, AppConfig.SftpPath + "/" + httpPostedFile.FileName,
                                      true);
                fin.Close();
                sftpClient.Disconnect();
            }
        }
    }
}

Points of Interest

  1. extensionFilter is used to allow only pdf, doc, docx and rtf documents.

    Image 1

  2. File size limit is also implemented by allowing file upto 5 MB.

    Image 2

  3. Number of files allowed in upload queue is also implemented by filter.

    Image 3

  4. In WebAPI, UploadedDocuments folder is created to get file into webserver from client.
  5. Drag and drop is also implemented along with default browse button.

For the complete source code, please see https://github.com/m-hassan-tariq/SFTPFileUploadUsingWebAPIandAngularJS.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United States United States
Five+ years of demonstrated work experience in developing and implementing business technology applications, systems integration and testing solutions with in-depth domain knowledge of industries like Healthcare, Telecom, Call Center, Financial Instruments, Payroll, HR, and skills including, but not limited to, software analysis, design and development.

Comprehensive understanding of NET Framework 4.5, 4.0, 2.0 and C#, ASP.Net, ADO.Net, Entity Framework, LINQ, Web Service, WCF, AJAX Control Toolkit, Advanced JavaScript, HTML 5.0, CSS3.0, jQuery, SSIS, SSRS, XML, XSLT, JSON.

Expertise in end to end development of enterprise web application and Single Page Application (SPA) using ASP.NET MVC, ASP.NET Web Forms, ASP.NET Web API, AngularJS, TypeScript, NodeJS, SQL Server and Design Pattern fanatic.

Comments and Discussions

 
Questiongetting some exception , Not able to upload multiple files on a single click. Pin
Member 135101195-Apr-18 22:05
Member 135101195-Apr-18 22:05 
Questionconfigure sftp Pin
ThomaLuke14-May-17 5:32
professionalThomaLuke14-May-17 5:32 
QuestionFile attachment using angular JS and ASP.NET MVC(upload and download) Pin
Junio Baba5-Jan-17 3:23
Junio Baba5-Jan-17 3:23 
QuestionUpload big files (Example : 20 or 30 Mo) Pin
Member 1251739117-May-16 22:51
Member 1251739117-May-16 22:51 
AnswerRe: Upload big files (Example : 20 or 30 Mo) Pin
Muhammad Hassan Tariq18-May-16 5:27
professionalMuhammad Hassan Tariq18-May-16 5:27 
QuestionShowing error in sftp connect Pin
Member 1037692330-Dec-15 22:02
Member 1037692330-Dec-15 22:02 
AnswerRe: Showing error in sftp connect Pin
Muhammad Hassan Tariq31-Dec-15 8:02
professionalMuhammad Hassan Tariq31-Dec-15 8:02 
Questiondoes not work on azure Pin
Kha Tran 213-Aug-15 4:53
Kha Tran 213-Aug-15 4:53 
AnswerRe: does not work on azure Pin
Kha Tran 214-Aug-15 15:30
Kha Tran 214-Aug-15 15:30 
GeneralRe: does not work on azure Pin
Muhammad Hassan Tariq14-Dec-15 7:52
professionalMuhammad Hassan Tariq14-Dec-15 7:52 
QuestionCan not upload Multiple files Pin
Member 110268262-Jul-15 0:34
Member 110268262-Jul-15 0:34 
AnswerRe: Can not upload Multiple files Pin
Kha Tran 213-Aug-15 3:21
Kha Tran 213-Aug-15 3:21 
GeneralRe: Can not upload Multiple files Pin
Muhammad Hassan Tariq14-Dec-15 7:52
professionalMuhammad Hassan Tariq14-Dec-15 7:52 
QuestionShowing percentage upload Pin
Kamina Moffey28-Apr-15 7:32
Kamina Moffey28-Apr-15 7:32 
AnswerRe: Showing percentage upload Pin
Muhammad Hassan Tariq28-Apr-15 7:49
professionalMuhammad Hassan Tariq28-Apr-15 7:49 
GeneralRe: Showing percentage upload Pin
Kamina Moffey28-Apr-15 11:55
Kamina Moffey28-Apr-15 11:55 
GeneralMy vote of 5 Pin
jaguar8427-Apr-15 5:00
professionaljaguar8427-Apr-15 5:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.