Real Time File Upload with SignalR in Asp.Net Core


In this article, I will show you how to implement real-time file upload in asp.net core using SignalR to send upload progress feedback.

The sample upload file is a CSV file and can be downloaded here as part of the source code.

Overview
I will be demonstrating how to upload a file in asp.net core using SignalR to send a real-time upload progress feedback to the client. Upload progress will be displayed by a bootstrap progress bar and a counter to display the number of records uploaded.

At the end of this writeup, you will learn
  • Uploading and processing file in asp.net core (CSV file as a case study)
  • Using SignalR to send real-time feedback to the client
  • Calling SignalR methods from server and client sides.

Prerequisites
  • Visual Studio 2019
  • ASP.Net Core 3.0 or latest

SignalR
SignalR is a Microsoft ASP.NET library that allows server code to send asynchronous notifications to client-side web applications. This allows developers to add real-time web functionality to their applications.
For further reading:
  • Lunch visual studio and click on create a new project.
  • In the create new project dialog, select ASP.NET Core Web Application and click next.
  • In the configure your new project dialog, name the project FileUpload then select create.
  • In the Create new ASP.NET Core web Application dialog, select .NET Core and ASP.NET Core 3.0 or latest. Select Web Application (Model-View-Controller) and click Create.
Modify the project Layout like so:

1:  <!DOCTYPE html>  
2:  <html lang="en">  
3:  <head>  
4:    <meta charset="utf-8" />  
5:    <meta name="viewport" content="width=device-width, initial-scale=1.0" />  
6:    <title>@ViewData["Title"] - File-Upload</title>  
7:    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />  
8:    <link rel="stylesheet" href="~/css/site.css" />  
9:  </head>  
10:  <body>  
11:    <header>  
12:      <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">  
13:        <div class="container">  
14:          <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">File-Upload</a>  
15:          <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"  
16:              aria-expanded="false" aria-label="Toggle navigation">  
17:            <span class="navbar-toggler-icon"></span>  
18:          </button>  
19:        </div>  
20:      </nav>  
21:    </header>  
22:    <div class="container">  
23:      <main role="main" class="pb-3">  
24:        @RenderBody()  
25:      </main>  
26:    </div>  
27:    <footer class="border-top footer text-muted">  
28:      <div class="container">  
29:        &copy; 2019 - FileUpload - <a asp-area="" asp-controller="Upload" asp-action="Privacy">Privacy</a>  
30:      </div>  
31:    </footer>  
32:    <script src="~/lib/jquery/dist/jquery.min.js"></script>  
33:    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>  
34:    <script src="~/js/site.js" asp-append-version="true"></script>  
35:    @RenderSection("Scripts", required: false)  
36:  </body>  
37:  </html>  

Add the SignalR client library
ASP.NET Core 3.0 shared framework included the SignalR server library but the JavaScript client library is not automatically included in the project. Hence, LibMan(Library Manager) will be used to get the client library from unpkg.

  • In Solution Explorer, right-click the project, select Add > Client-Side Library.
  • In the Add Client-Side Library dialog, select unpkg as the provider.
  • Type @microsoft/signlr@latest for Library.
  • Select Choose specific files, expand the dist/browser folder, and select signalr.js and signalr.min.js.
  • Set Target Location to wwwroot/js/signalr/, and select Install. 



Create a SignalR Hub
  • Right-click the project, select Add > new folder. Name the folder Hubs.
  • In the Hubs folder, create a BroadCastHub.cs class 
  • Modify the BroadCastHub like so:

1:  using Microsoft.AspNetCore.SignalR;  
2:  using System.Threading.Tasks;  
3:  namespace FileUpload.Hubs  
4:  {  
5:    public class BroadCastHub: Hub  
6:    {  
7:      public Task Feedback(string message)  
8:      {  
9:       return Clients.Caller.SendAsync(&quot;feedBack&quot;, message);  
10:      }  
11:    }  
12:  }  

Configure SignalR
Open and modify the Startup.cs file like so:

1:  using FileUpload.Hubs;  
2:  using Microsoft.AspNetCore.Builder;  
3:  using Microsoft.AspNetCore.Hosting;  
4:  using Microsoft.Extensions.Configuration;  
5:  using Microsoft.Extensions.DependencyInjection;  
6:  using Microsoft.Extensions.Hosting;  
7:  namespace FileUpload  
8:  {  
9:    public class Startup  
10:    {  
11:      public Startup(IConfiguration configuration)  
12:      {  
13:        Configuration = configuration;  
14:      }  
15:      public IConfiguration Configuration { get; }  
16:      // This method gets called by the runtime. Use this method to add services to the container.  
17:      public void ConfigureServices(IServiceCollection services)  
18:      {  
19:        services.AddControllersWithViews();  
20:        services.AddSignalR();  
21:      }  
22:      // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.  
23:      public void Configure(IApplicationBuilder app, IWebHostEnvironment env)  
24:      {  
25:        if (env.IsDevelopment())  
26:        {  
27:          app.UseDeveloperExceptionPage();  
28:        }  
29:        else  
30:        {  
31:          app.UseExceptionHandler("/Home/Error");  
32:          // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.  
33:          app.UseHsts();  
34:        }  
35:        app.UseHttpsRedirection();  
36:        app.UseStaticFiles();  
37:        app.UseRouting();  
38:        app.UseAuthorization();  
39:        app.UseEndpoints(endpoints =>  
40:        {  
41:          endpoints.MapControllerRoute(  
42:            name: "default",  
43:            pattern: "{controller=Upload}/{action=Index}/{id?}");  
44:          endpoints.MapHub<BroadCastHub>("/broadcast");  
45:        });  
46:      }  
47:    }  
48:  }  

Add File Upload Client Code
  • Right-click the controllers folder and select Add > controller > MVC Controller - Empty, then click Add.
  • Name the controller Upload then click  Add.
  • Right-click the Index action then select Add view.
  • Modify the index view like so:

1:  @{  
2:    ViewData["Title"] = "File Upload";  
3:  }  
4:  <style>  
5:    .fileUpload {  
6:      position: relative;  
7:      overflow: hidden;  
8:      margin: 0;  
9:    }  
10:    .fileUpload input.upload {  
11:      position: absolute;  
12:      top: 0;  
13:      right: 0;  
14:      margin: 0;  
15:      padding: 0;  
16:      font-size: 20px;  
17:      cursor: pointer;  
18:      opacity: 0;  
19:      filter: alpha(opacity=0);  
20:    }  
21:  </style>  
22:  <div class="row">  
23:  <div class="col-md-6">  
24:  <div class="input-group">  
25:  <input class="form-control" disabled="disabled" id="uploadFile" placeholder="" style="height: 36px;" />  
26:        <br />  
27:  <div class="input-group-btn">  
28:  <div class="fileUpload btn btn-info">  
29:  ...  
30:            <input accept=".csv" class="upload" id="fileToUpload" multiple="" name="fileToUpload" onchange="fileToUploadOnchange()" type="file" />  
31:          </div>  
32:  </div>  
33:  </div>  
34:  </div>  
35:  <input class="btn btn-info btn-sm" id="btnUpload" type="button" value="Upload" />  
36:  </div>  
37:  <div class="text-center">  
38:  <div class="row">  
39:  <div class="col-md-12">  
40:  <table class="table table-bordered table-hover" style="width: 100%;">  
41:          <thead>  
42:  <tr>  
43:              <th>S/N</th>  
44:              <th>Company</th>  
45:              <th>Category</th>  
46:              <th>City</th>  
47:              <th>Funded Date</th>  
48:              <th>Currency</th>  
49:              <th>Amount</th>  
50:            </tr>  
51:  </thead>  
52:          <tbody id="tableBody">  
53:          </tbody>  
54:        </table>  
55:  </div>  
56:  </div>  
57:  </div>  
58:  <div class="modal fade" data-backdrop="static" id="progressModal" role="dialog" tabindex="-1">  
59:  <div class="modal-dialog modal-lg" style="top: 40vh; width: 50%;">  
60:  <div class="modal-content">  
61:  <div class="modal-body">  
62:  <div class="row">  
63:  <div class="col-md-10">  
64:  <div class="progress">  
65:  <div aria-valuemax="100" aria-valuemin="0" aria-valuenow="0" class="progress-bar progress-bar-striped" id="progress_bar" role="progressbar" style="width: 0%;">  
66:  </div>  
67:  </div>  
68:  </div>  
69:  <div class="col-md-2">  
70:  Count: <text id="uploadCount">0</text>  
71:            </div>  
72:  </div>  
73:  </div>  
74:  </div>  
75:  <!-- / .modal-content -->  
76:    </div>  
77:  <!-- / .modal-dialog -->  
78:  </div>  
79:  <!-- / .modal -->  
80:  </pre>  
81:  </div>  
82:  <br />  
83:  <div>  
84:  <b>Add SignalR to Client Code</b></div>  
85:  <div>  
86:  Modify the script section of the view like so:  
87:  <br />  
88:  <div>  
89:  <br />  
90:  @section Scripts{  
91:    <script src="~/js/signalr/dist/browser/signalr.js"></script>  
92:    <script>  
93:      var connection = new signalR.HubConnectionBuilder().withUrl("/broadcast").build();  
94:      connection.on("feedBack", function (model) {  
95:        $("#progress_bar").css("width", `${model.currentPercent}%`);  
96:        $("#uploadCount").html(model.uploadCount);  
97:      });  
98:      connection.start().then(function ()  
99:      {  
100:        console.log("Sinalr Connected");  
101:      }).catch(function (err) {  
102:        console.log(err.toString());  
103:      });  
104:    </script>  
105:  }  

The above script added reference to the SignalR js file we added using LibMan. Also, the client SignalR connection is configured and started. An event listener feedBack is added to the connection which listens to the message from the server. Upon message received, the width property of the progress bar and the upload count is updated. The event listener feedBack is the method name passed to the SendAsync in the BroadCastHub.

Add Models
  •  Right-click the models folder, select Add > class. Name the class RecordItem. 
  • Modify the file like so:
1:  namespace FileUpload.Models  
2:  {  
3:    public class RecordItem  
4:    {  
5:      public string Company { get; set; }  
6:      public string Date { get; set; }  
7:      public string Category { get; set; }  
8:      public string City { get; set; }  
9:      public string Currency { get; set; }  
10:      public decimal Amount { get; set; }  
11:      public string StrAmount  
12:      {  
13:        get  
14:        {  
15:          return Amount.ToString("#,##0.00;(#,##0.00)");  
16:        }  
17:      }  
18:    }  
19:  }  

Also, add FeedbackModel to the models folder and modify like so:

1:  using System;  
2:  using System.Collections.Generic;  
3:  namespace FileUpload.Models  
4:  {  
5:    public class FeedbackModel  
6:    {  
7:      public int currentCount { get; set; }  
8:      public string currentPercent { get; set; }  
9:      public int UploadCount { get; set; }  
10:    }  
11:  }  

Add the Upload Functionality to the Controller
Inject the IHubContext like so:
1:   private readonly IHubContext<BroadCastHub> hubContext;  
2:      public UploadController(IHubContext<BroadCastHub> _hubContext)  
3:      {  
4:        hubContext = _hubContext;  
5:      }  

Add a private method to handle feedback broadcasting from the controller like so:
1:  private async void SendFeedBack(int currentCount, int UploadCount)  
2:     {  
3:        var totalCount = 4;  
4:        var feedBackModel = new FeedbackModel()  
5:        {  
6:          currentCount = currentCount,  
7:          currentPercent = (currentCount * 100 / totalCount).ToString(),  
8:          UploadCount = UploadCount,  
9:        };  
10:        await hubContext.Clients.All.SendAsync("feedBack", feedBackModel);  
11:    }  

Next, add the file upload method like so:
1:   public async Task<JsonResult> UploadFile()  
2:      {  
3:        try  
4:        {  
5:          var counter = 0;  
6:          var currentCount = 0;  
7:          var data = new List<RecordItem>();  
8:          var postedFile = Request.Form.Files;  
9:          await Task.Delay(500);  
10:          currentCount++;  
11:          SendFeedBack(currentCount, counter);  
12:          if (postedFile.Count <= 0 || postedFile == null)  
13:            return Json(new { error = true, message = "Empty File was uploaded" });  
14:          if (postedFile[0] == null || postedFile[0].Length <= 0)  
15:          {  
16:            return Json(new { error = true, message = "Empty File was uploaded" });  
17:          }  
18:          await Task.Delay(500);  
19:          currentCount++;  
20:          SendFeedBack(currentCount, counter);  
21:          var fileInfo = new FileInfo(postedFile[0].FileName);  
22:          var extention = fileInfo.Extension;  
23:          if (extention.ToLower() != ".csv")  
24:          {  
25:            return Json(new { error = true, message = "invalid file format" });  
26:          }  
27:          using (StreamReader sr = new StreamReader(postedFile[0].OpenReadStream()))  
28:          {  
29:            await Task.Delay(500);  
30:            currentCount++;  
31:            SendFeedBack(currentCount, counter);  
32:            while (!sr.EndOfStream)  
33:            {  
34:              String Info = sr.ReadLine();  
35:              String[] Records;  
36:              if (Info.Contains('\"'))  
37:              {  
38:                var row = string.Empty;  
39:                var model = Info.Replace("\"", "#*").Split('#');  
40:                foreach (var item in model)  
41:                {  
42:                  var d = item.Replace("*,", ",");  
43:                  if (d.Contains("*"))  
44:                  {  
45:                    row += d.Replace("*", "").Replace(",", "");  
46:                  }  
47:                  else  
48:                  {  
49:                    row += d;  
50:                  }  
51:                }  
52:                Records = new String[row.Split(new char[] { ',' }).Length];  
53:                row.Split(new char[] { ',' }).CopyTo(Records, 0);  
54:              }  
55:              else  
56:              {  
57:                Records = new String[Info.Split(new char[] { ',' }).Length];  
58:                Info.Split(new char[] { ',' }).CopyTo(Records, 0);  
59:              }  
60:              var strAmount = Records[7].ToString().Trim();  
61:              decimal output;  
62:              if (string.IsNullOrEmpty(strAmount) || !decimal.TryParse(strAmount, out output)) continue;  
63:              var datafile = new RecordItem()  
64:              {  
65:                Company = Records[1].ToString().Trim(),  
66:                Category = Records[3].ToString().Trim(),  
67:                City = Records[4].ToString().Trim(),  
68:                Date = Records[6].ToString().Trim(),  
69:                Currency = Records[8].ToString().Trim(),  
70:                Amount = decimal.Parse(Records[7].ToString().Trim()),  
71:              };  
72:              data.Add(datafile);  
73:              counter++;  
74:              SendFeedBack(currentCount, counter);  
75:            }  
76:            sr.Close();  
77:            sr.Dispose();  
78:            await Task.Delay(500);  
79:            currentCount++;  
80:            SendFeedBack(currentCount, counter);  
81:          }  
82:          await Task.Delay(500);  
83:          currentCount++;  
84:          SendFeedBack(currentCount, counter);  
85:          return Json(new { error = false, data = data });  
86:        }  
87:        catch (Exception ex)  
88:        {  
89:          return Json(new  
90:          {  
91:            error = true,  
92:            message = ex.InnerException != null ?  
93:            ex.InnerException.Message : ex.Message  
94:          });  
95:        }  
96:      }  

Note that the Task.Delay(500) was used, this is because we want to add some delays to the upload in other to see the SignalR feedback in action. Do not use this approach in a real application unless otherwise needed.

Wrap Up Client Upload View
To complete the file upload, add the following to the script section:
1:   $(document).ready(function () {  
2:        defaultTableContent();  
3:      })  
4:      $("#btnUpload").click(function (e) {  
5:        e.preventDefault();  
6:        uploadFile();  
7:      });  
8:      function defaultTableContent() {  
9:        var empltyTableTemplate = $("<tr></tr>");  
10:        var placeholder = "<td colSpan='7'><p>No Data</p></td>";  
11:        empltyTableTemplate.html(placeholder);  
12:        $("#tableBody").html(empltyTableTemplate);  
13:      }  
14:      function fileToUploadOnchange() {  
15:        $this = document.getElementById("fileToUpload");  
16:        fileSelected($this);  
17:      }  
18:      function fileSelected(input) {  
19:        var file = document.getElementById('fileToUpload').files[0];  
20:        var ext = input.value.split('.');  
21:        ext = ext[ext.length - 1].toLowerCase();  
22:        var arrayExtensions = ['csv'];  
23:        if (arrayExtensions.lastIndexOf(ext.toLowerCase()) == -1) {  
24:          alert("You can only upload .CSV File");  
25:          input.value = '';  
26:          document.getElementById("uploadFile").value = '';  
27:          return;  
28:        }  
29:        document.getElementById("uploadFile").value = file.name;  
30:        if (file) {  
31:          var fileSize = 0;  
32:          if (file.size > 1024 * 1024)  
33:            fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';  
34:          else  
35:            fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';  
36:        }  
37:      }  
38:      function uploadFile() {  
39:        var file = document.getElementById('fileToUpload').files[0];  
40:        if (file) {  
41:          $("#progressModal").modal("show");  
42:          var url = '@Url.Action("UploadFile", "Upload")';  
43:          var fd = new FormData();  
44:          fd.append("fileToUpload", file);  
45:          var xhr = new XMLHttpRequest();  
46:          xhr.addEventListener("load", uploadComplete, false);  
47:          xhr.addEventListener("error", uploadFailed, false);  
48:          xhr.addEventListener("abort", uploadCanceled, false);  
49:          xhr.open("POST", url);  
50:          xhr.send(fd);  
51:        } else {  
52:          alert("You are can not upload an empty file");  
53:        }  
54:      }  
55:      function uploadComplete(evt) {  
56:        var response = JSON.parse(evt.target.response);  
57:         $("#progressModal").modal("hide");  
58:         ResetUploadModal();  
59:        if (response.error) {  
60:          alert(response.message);  
61:          return;  
62:        }  
63:        populateTableData(response.data);  
64:      }  
65:      function populateTableData(data) {  
66:        if (data.length <= 0) return;  
67:          $("#tableBody").html('');  
68:        $.each(data, function (i, val) {  
69:          let tableRow = $("<tr></tr>");  
70:          let tablecells = `<td>${i + 1}</td>` +  
71:            `<td>${val.company}</td>` +  
72:            `<td>${val.category}</td>` +  
73:            `<td>${val.city}</td>` +  
74:            `<td>${val.date}</td>` +  
75:            `<td>${val.currency}</td>` +  
76:            `<td>${val.strAmount}</td>`;  
77:          tableRow.html(tablecells);  
78:          $("#tableBody").append(tableRow[0]);  
79:        });  
80:      }  
81:      function uploadFailed(evt) {  
82:        $("#progressModal").modal("hide");  
83:        ResetUploadModal();  
84:        alert("There was an error attempting to upload the file");  
85:      }  
86:      function uploadCanceled(evt) {  
87:        $("#progressModal").modal("hide");  
88:        ResetUploadModal();  
89:        alert("The upload has been canceled by the user or the browser dropped the connection")  
90:      }  
91:      function ResetUploadModal() {  
92:        document.getElementById("uploadFile").value = '';  
93:        document.getElementById("fileToUpload").value = null;  
94:        $("#progressContainer").hide();  
95:        $("#progress_bar").css("width", "-1%");  
96:      }  

The Complete view should look like so:
1:  @{  
2:    ViewData["Title"] = "File Upload";  
3:  }  
4:  <style>  
5:    .fileUpload {  
6:      position: relative;  
7:      overflow: hidden;  
8:      margin: 0;  
9:    }  
10:    .fileUpload input.upload {  
11:      position: absolute;  
12:      top: 0;  
13:      right: 0;  
14:      margin: 0;  
15:      padding: 0;  
16:      font-size: 20px;  
17:      cursor: pointer;  
18:      opacity: 0;  
19:      filter: alpha(opacity=0);  
20:    }  
21:  </style>  
22:  <div class="row">  
23:    <div class="col-md-6">  
24:      <div class="input-group">  
25:        <input id="uploadFile" placeholder="" disabled="disabled" class="form-control" style="height:36px" />  
26:        <div class="input-group-btn">  
27:          <div class="fileUpload btn btn-info">  
28:            <span>...</span>  
29:            <input type="file" name="fileToUpload" id="fileToUpload" class="upload" onchange="fileToUploadOnchange()" multiple accept=".csv" />  
30:          </div>  
31:        </div>  
32:      </div>  
33:    </div>  
34:    <input type="button" class="btn btn-info btn-sm" value="Upload" id="btnUpload" />  
35:  </div>  
36:  <p></p>  
37:  <div class="text-center">  
38:    <div class="row">  
39:      <div class="col-md-12">  
40:        <table class="table table-bordered table-hover" style="width:100%">  
41:          <thead>  
42:            <tr>  
43:              <th>S/N</th>  
44:              <th>Company</th>  
45:              <th>Category</th>  
46:              <th>City</th>  
47:              <th>Funded Date</th>  
48:              <th>Currency</th>  
49:              <th>Amount</th>  
50:            </tr>  
51:          </thead>  
52:          <tbody id="tableBody">  
53:          </tbody>  
54:        </table>  
55:      </div>  
56:    </div>  
57:  </div>  
58:  <div id="progressModal" class="modal fade modal-center-c" tabindex="-1" role="dialog" data-backdrop="static">  
59:    <div class="modal-dialog modal-lg" style="width: 50% !important;  top: 40vh;">  
60:      <div class="modal-content">  
61:        <div class="modal-body">  
62:          <div class="row">  
63:            <div class="col-md-10">  
64:              <div class="progress">  
65:                <div class="progress-bar progress-bar-striped" role="progressbar" id="progress_bar" style="width: 0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>  
66:              </div>  
67:            </div>  
68:            <div class="col-md-2">  
69:               <span>Count: <text id="uploadCount">0</text></span>  
70:            </div>  
71:          </div>  
72:        </div>  
73:      </div> <!-- / .modal-content -->  
74:    </div> <!-- / .modal-dialog -->  
75:  </div> <!-- / .modal -->  
76:  @section Scripts{  
77:    <script src="~/js/signalr/dist/browser/signalr.js"></script>  
78:    <script>  
79:      var connection = new signalR.HubConnectionBuilder().withUrl("/broadcast").build();  
80:      connection.on("feedBack", function (model) {  
81:        $("#progress_bar").css("width", `${model.currentPercent}%`);  
82:        $("#uploadCount").html(model.uploadCount);  
83:      });  
84:      connection.start().then(function ()  
85:      {  
86:        console.log("Sinalr Connected");  
87:      }).catch(function (err) {  
88:        console.log(err.toString());  
89:      });  
90:      $(document).ready(function () {  
91:        defaultTableContent();  
92:      })  
93:      $("#btnUpload").click(function (e) {  
94:        e.preventDefault();  
95:        uploadFile();  
96:      });  
97:      function defaultTableContent() {  
98:        var empltyTableTemplate = $("<tr></tr>");  
99:        var placeholder = "<td colSpan='7'><p>No Data</p></td>";  
100:        empltyTableTemplate.html(placeholder);  
101:        $("#tableBody").html(empltyTableTemplate);  
102:      }  
103:      function fileToUploadOnchange() {  
104:        $this = document.getElementById("fileToUpload");  
105:        fileSelected($this);  
106:      }  
107:      function fileSelected(input) {  
108:        var file = document.getElementById('fileToUpload').files[0];  
109:        var ext = input.value.split('.');  
110:        ext = ext[ext.length - 1].toLowerCase();  
111:        var arrayExtensions = ['csv'];  
112:        if (arrayExtensions.lastIndexOf(ext.toLowerCase()) == -1) {  
113:          alert("You can only upload .CSV File");  
114:          input.value = '';  
115:          document.getElementById("uploadFile").value = '';  
116:          return;  
117:        }  
118:        document.getElementById("uploadFile").value = file.name;  
119:        if (file) {  
120:          var fileSize = 0;  
121:          if (file.size > 1024 * 1024)  
122:            fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';  
123:          else  
124:            fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';  
125:        }  
126:      }  
127:      function uploadFile() {  
128:        var file = document.getElementById('fileToUpload').files[0];  
129:        if (file) {  
130:          $("#progressModal").modal("show");  
131:          var url = '@Url.Action("UploadFile", "Upload")';  
132:          var fd = new FormData();  
133:          fd.append("fileToUpload", file);  
134:          var xhr = new XMLHttpRequest();  
135:          xhr.addEventListener("load", uploadComplete, false);  
136:          xhr.addEventListener("error", uploadFailed, false);  
137:          xhr.addEventListener("abort", uploadCanceled, false);  
138:          xhr.open("POST", url);  
139:          xhr.send(fd);  
140:        } else {  
141:          alert("You are can not upload an empty file");  
142:        }  
143:      }  
144:      function uploadComplete(evt) {  
145:        var response = JSON.parse(evt.target.response);  
146:         $("#progressModal").modal("hide");  
147:         ResetUploadModal();  
148:        if (response.error) {  
149:          alert(response.message);  
150:          return;  
151:        }  
152:        populateTableData(response.data);  
153:      }  
154:      function populateTableData(data) {  
155:        if (data.length <= 0) return;  
156:          $("#tableBody").html('');  
157:        $.each(data, function (i, val) {  
158:          let tableRow = $("<tr></tr>");  
159:          let tablecells = `<td>${i + 1}</td>` +  
160:            `<td>${val.company}</td>` +  
161:            `<td>${val.category}</td>` +  
162:            `<td>${val.city}</td>` +  
163:            `<td>${val.date}</td>` +  
164:            `<td>${val.currency}</td>` +  
165:            `<td>${val.strAmount}</td>`;  
166:          tableRow.html(tablecells);  
167:          $("#tableBody").append(tableRow[0]);  
168:        });  
169:      }  
170:      function uploadFailed(evt) {  
171:        $("#progressModal").modal("hide");  
172:        ResetUploadModal();  
173:        alert("There was an error attempting to upload the file");  
174:      }  
175:      function uploadCanceled(evt) {  
176:        $("#progressModal").modal("hide");  
177:        ResetUploadModal();  
178:        alert("The upload has been canceled by the user or the browser dropped the connection")  
179:      }  
180:      function ResetUploadModal() {  
181:        document.getElementById("uploadFile").value = '';  
182:        document.getElementById("fileToUpload").value = null;  
183:        $("#progressContainer").hide();  
184:        $("#progress_bar").css("width", "-1%");  
185:      }  
186:    </script>  
187:  }  

The Controller should look like so:
1:  using System;  
2:  using System.Collections.Generic;  
3:  using System.Threading.Tasks;  
4:  using Microsoft.AspNetCore.Mvc;  
5:  using FileUpload.Models;  
6:  using System.IO;  
7:  using FileUpload.Hubs;  
8:  using Microsoft.AspNetCore.SignalR;  
9:  namespace FileUpload.Controllers  
10:  {  
11:    public class UploadController : Controller  
12:    {  
13:      private readonly IHubContext<BroadCastHub> hubContext;  
14:      public UploadController(IHubContext<BroadCastHub> _hubContext)  
15:      {  
16:        hubContext = _hubContext;  
17:      }  
18:      public IActionResult Index()  
19:      {  
20:        return View();  
21:      }  
22:      public async Task<JsonResult> UploadFile()  
23:      {  
24:        try  
25:        {  
26:          var counter = 0;  
27:          var currentCount = 0;  
28:          var data = new List<RecordItem>();  
29:          var postedFile = Request.Form.Files;  
30:          await Task.Delay(500);  
31:          currentCount++;  
32:          SendFeedBack(currentCount, counter);  
33:          if (postedFile.Count <= 0 || postedFile == null)  
34:            return Json(new { error = true, message = "Empty File was uploaded" });  
35:          if (postedFile[0] == null || postedFile[0].Length <= 0)  
36:          {  
37:            return Json(new { error = true, message = "Empty File was uploaded" });  
38:          }  
39:          await Task.Delay(500);  
40:          currentCount++;  
41:          SendFeedBack(currentCount, counter);  
42:          var fileInfo = new FileInfo(postedFile[0].FileName);  
43:          var extention = fileInfo.Extension;  
44:          if (extention.ToLower() != ".csv")  
45:          {  
46:            return Json(new { error = true, message = "invalid file format" });  
47:          }  
48:          using (StreamReader sr = new StreamReader(postedFile[0].OpenReadStream()))  
49:          {  
50:            await Task.Delay(500);  
51:            currentCount++;  
52:            SendFeedBack(currentCount, counter);  
53:            while (!sr.EndOfStream)  
54:            {  
55:              String Info = sr.ReadLine();  
56:              String[] Records;  
57:              if (Info.Contains('\"'))  
58:              {  
59:                var row = string.Empty;  
60:                var model = Info.Replace("\"", "#*").Split('#');  
61:                foreach (var item in model)  
62:                {  
63:                  var d = item.Replace("*,", ",");  
64:                  if (d.Contains("*"))  
65:                  {  
66:                    row += d.Replace("*", "").Replace(",", "");  
67:                  }  
68:                  else  
69:                  {  
70:                    row += d;  
71:                  }  
72:                }  
73:                Records = new String[row.Split(new char[] { ',' }).Length];  
74:                row.Split(new char[] { ',' }).CopyTo(Records, 0);  
75:              }  
76:              else  
77:              {  
78:                Records = new String[Info.Split(new char[] { ',' }).Length];  
79:                Info.Split(new char[] { ',' }).CopyTo(Records, 0);  
80:              }  
81:              var strAmount = Records[7].ToString().Trim();  
82:              decimal output;  
83:              if (string.IsNullOrEmpty(strAmount) || !decimal.TryParse(strAmount, out output)) continue;  
84:              var datafile = new RecordItem()  
85:              {  
86:                Company = Records[1].ToString().Trim(),  
87:                Category = Records[3].ToString().Trim(),  
88:                City = Records[4].ToString().Trim(),  
89:                Date = Records[6].ToString().Trim(),  
90:                Currency = Records[8].ToString().Trim(),  
91:                Amount = decimal.Parse(Records[7].ToString().Trim()),  
92:              };  
93:              data.Add(datafile);  
94:              counter++;  
95:              SendFeedBack(currentCount, counter);  
96:            }  
97:            sr.Close();  
98:            sr.Dispose();  
99:            await Task.Delay(500);  
100:            currentCount++;  
101:            SendFeedBack(currentCount, counter);  
102:          }  
103:          await Task.Delay(500);  
104:          currentCount++;  
105:          SendFeedBack(currentCount, counter);  
106:          return Json(new { error = false, data = data });  
107:        }  
108:        catch (Exception ex)  
109:        {  
110:          return Json(new  
111:          {  
112:            error = true,  
113:            message = ex.InnerException != null ?  
114:            ex.InnerException.Message : ex.Message  
115:          });  
116:        }  
117:      }  
118:      private async void SendFeedBack(int currentCount, int UploadCount)  
119:      {  
120:        var totalCount = 4;  
121:        var feedBackModel = new FeedbackModel()  
122:        {  
123:          currentCount = currentCount,  
124:          currentPercent = (currentCount * 100 / totalCount).ToString(),  
125:          UploadCount = UploadCount,  
126:        };  
127:        await hubContext.Clients.All.SendAsync("feedBack", feedBackModel);  
128:      }  
129:      public IActionResult Privacy()  
130:      {  
131:        return View();  
132:      }  
133:    }  
134:  }  

Build and run the solution. Select and upload the sample CSV file included in the source code.

You can download the source code from here.

Kindly drop your comments.

Thank you for your time.
Real Time File Upload with SignalR in Asp.Net Core Real Time File Upload with SignalR in Asp.Net Core Reviewed by Akintunde Toba on June 08, 2019 Rating: 5

No comments:

Home Ads

Powered by Blogger.