Introduction



// Improt the following namespace
using System.IO;
[HttpPost]
public async Task<IActionResult> Upload([FromForm]IFormFile file)
{
// Check if thefile is there
if (file == null)
return BadRequest("File is required");
// Get the file name
var fileName = file.FileName;
// Get the extension
var extension = Path.GetExtension(fileName);
// Validate the extension based on your business needs
// Generate a new file to avoid dublicates = (FileName withoutExtension - GUId.extension)
var newFileName = $"{Path.GetFileNameWithoutExtension(fileName)}-{Guid.NewGuid().ToString()}{extension}";
// Create the full path of the file including the directory (For this demo we will save the file insidea folder called Data within wwwroot)
var directoryPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "Data");
var fullPath = Path.Combine(directoryPath, newFileName);
// Maek sure the directory is ther bycreating it if it's not exist
Directory.CreateDirectory(directoryPath);
// Create a new file stream where you want to put your file and copy the content from the current file stream to the new one
using (var fileStream = new FileStream(fullPath, FileMode.Create, FileAccess.Write))
{
// Copy the content to the new stream
await file.CopyToAsync(fileStream);
}
// You are done return the new URL which is (yourapplication url/data/newfilename)
return Ok($"https://localhost:44302/Data/{newFileName}");
}
Now we can access that endpoint, through the following URL "POST: https://yourapplication/api/files"

using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace UploadFileWithProgress.Client.Pages
{
public partial class Index
{
}
}
Now let's move to the Index.razor component and create a simple input form with a button to send that file to the server as follows.
The following code has a sample UI for an InputFile, label, and an upload button, InputFile is the new input component by Microsoft to handle the process of choosing a file to be uploaded,
@page "/"
<h1>Welcome to C# Corner</h1>
<p>Learn how to upload a file from Blazor WebAssembly to the an ASP.NET Core Web API with progress</p>
<div class="row">
<div class="col-4">
<div class="form-group">
<label>File</label>
<InputFile OnChange="OnChooseFile" />
<p>0KB / 9,500KB</p>
</div>
<button class="btn btn-success btn-block m-1">Upload</button>
</div>
</div>
Also in the code-behind file create the following method "OnChooseFile" which is the method that will handle choosing the file by the InputFile component and make sure to import the namespace "Microsoft.AspNetCore.Components.Forms" like the following code,
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace UploadFileWithProgress.Client.Pages
{
public partial class Index
{
public void OnChooseFile(InputFileChangeEventArgs e)
{
}
}
}
Now if we run the project we should see something like this,

// MAke sure to import the namespace
using System.IO;
// Create a global variable that will be used by OnChooseFile and UploadAsync methods
private Stream _fileStream = null;
private string _selectedFileName = null;
public void OnChooseFile(InputFileChangeEventArgs e)
{
// Get the selected file
var file = e.File;
// Check if the file is null then return from the method
if (file == null)
return;
// Validate the extension if requried (Client-Side)
// Set the value of the stream by calling OpenReadStream and pass the maximum number of bytes allowed because by default it only allows 512KB
// I used the value 5000000 which is about 50MB
using (var stream = file.OpenReadStream(50000000))
{
_fileStream = stream;
_selectedFileName = file.Name;
}
}
So after we created that function we are able now to move to the other part which is uploading but before we doing so let's explain a little bit how the uploading process happens so we know how we can make it progressive.
public class ProgressiveStreamContent : StreamContent
{
// Define the variables which is the stream that represents the file
private readonly Stream _fileStream;
// Maximum amount of bytes to send per packet
private readonly int _maxBuffer = 1024 * 4;
public ProgressiveStreamContent(Stream stream, int maxBuffer, Action<long, double> onProgress) : base(stream)
{
_fileStream = stream;
_maxBuffer = maxBuffer;
OnProgress += onProgress;
}
/// <summary>
/// Event that we can subscribe to which will be triggered everytime after part of the file gets uploaded.
/// It passes the total amount of uploaded bytes and the percentage as well
/// </summary>
public event Action<long, double> OnProgress;
// Override the SerialzeToStreamAsync method which provides us with the stream that we can write our chunks into it
protected async override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
// Define an array of bytes with the the length of the maximum amount of bytes to be pushed per time
var buffer = new byte[_maxBuffer];
var totalLength = _fileStream.Length;
// Variable that holds the amount of uploaded bytes
long uploaded = 0;
// Create an while loop that we will break it internally when all bytes uploaded to the server
while (true)
{
using (_fileStream)
{
// In this part of code here in every loop we read a chunk of bytes and write them to the stream of the HttpContent
var length = await _fileStream.ReadAsync(buffer, 0, _maxBuffer);
// Check if the amount of bytes read recently, if there is no bytes read break the loop
if (length <= 0)
{
break;
}
// Add the amount of read bytes to uploaded variable
uploaded += length;
// Calculate the percntage of the uploaded bytes out of the total remaining
var perentage = Convert.ToDouble(uploaded * 100 / _fileStream.Length);
// Write the bytes to the HttpContent stream
await stream.WriteAsync(buffer);
// Fire the event of OnProgress to notify the client about progress so far
OnProgress?.Invoke(uploaded, percentage);
// Add this delay over here just to simulate to notice the progress, because locally it's going to be so fast that you can barely notice it
await Task.Delay(250);
}
}
}
}
Actually, this is all you need to do, the last step is just to submit or file to the server and use our new class and show the progress in the UI so let's go ahead and do that.
Now back to the Index.razor.cs the code-behind file.
Let's create two local fields to be used in the UI to show the percentage and the number of uploaded bytes and then write the method that will upload the file to the server (Code is self-explanatory).
private long _uploaded = 0;
private double _percentage = 0;
// The method that will submit the file to the server
public async Task SubmitFileAsync()
{
// Create a mutlipart form data content which will hold the key value of the file that must be of type StreamContent
var content = new MultipartFormDataContent();
// Create an instance of ProgressiveStreamContent that we just created and we will pass the stream of the file for it
// and the 40096 which are 40KB per packet and the third argument which as a callback for the OnProgress event (u, p) are u = Uploaded bytes and P is the percentage
var streamContent = new ProgressiveStreamContent(_fileStream, 40096, (u, p) =>
{
// Set the values of the _uploaded & _percentage fields to the value provided from the event
_uploaded = u;
_percentage = p;
// Call StateHasChanged() to notify the component about this change to re-render the UI
StateHasChanged();
});
// Add the streamContent with the name to the FormContent variable
content.Add(streamContent, "File");
// Submit the request
var response = await Client.PostAsync("/weatherforecast", streamContent);
}
Now in the index.razor make the changes to call the Upload method and set the values of the percentage & uploaded/total KB as following,
<div class="row">
<div class="col-4">
<div class="form-group">
<label>File</label>
<InputFile OnChange="OnChooseFile" />
@* Show the value of the _uploaded variable divided to 1024 to show the amount in KB and also we use _fileStream?.Length / 1024 to show the total amount of KBs *@
<p>@(_uploaded / 1024)KB / @(_fileStream?.Length / 1024)KB</p>
@* Show the percentage of uploaded amount of of the total *@
<p>Percentage: @_percentage %</p>
</div>
@* Call the SubmitFileAsync in the @onclick event *@
<button type="submit" class="btn btn-success btn-block m-1" @onclick="SubmitFileAsync">Upload</button>
</div>
</div>
Now we are ready to go' let's run the project and choose a file like about 40MB and see the progress in real-time, you should see something like this:

I hope you enjoyed this tutorial and you are able to apply the concept in your application in your style and way
The code is available on GitHub at this link.

Hazrat AliPosted Jul 16, 2023, 12:39 AM
I tried my best and spent a couple of hours on it but this solutions is not working for me. First there was error in using 'using ' keyword which i solved, now it post the multipart data from browser apparently but the progress is quicker (prompt) than we can see and control in network tab. Plus, I could not find a way to get the data on controller side, which seems empty, and also the header content type is always incorrect ( which i managed to solve but of no use).
kumaragurubaran muthukumaraswamyPosted Jul 5, 2022, 7:40 AM
GIT link is not found. Please share the project
Osh SilvaPosted Apr 14, 2022, 5:22 AM
Hi Ahmad, where is Client.PostAsync method written. I can't see it in the GitHub project also
Benny AdiwijayaPosted Feb 27, 2022, 9:56 PM
Hi, github link is not found, can you share the project?
Pankaj GoelPosted Nov 29, 2021, 3:06 PM
When i upload my excel file (size 10KB). I am getting below error after Setting the value of the stream by calling OpenReadStream.I am new to blazor Blazor.server.js:1 [2021-11-29T15:03:55.892Z] Error: System.Text.Json.JsonException: Invalid JSON at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.EndInvokeJS(JSRuntime jsRuntime, String arguments) at Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost.<>c__DisplayClass43_0.<EndInvokeJSFromDotNet>b__0() at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.<>c.<InvokeAsync>b__8_0(Object state) --- End of stack trace from previous location --- at Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost.EndInvokeJSFromDotNet(Int64 asyncCall, Boolean succeeded, String arguments)
Ted KuoPosted Sep 15, 2021, 6:23 PM
I tried to use your ProgressiveStreamContent class. It works fine for small files (< 1MB). But when I tried with 3 MB file, it throws System.OperationCanceledException exception while executing SerializeToStreamAsync after processing about 1 MB of the file. I wonder if there is a default limit that I am hitting. If I do not use ProgressiveStreamContent class, and simply do "var streamContent = new StreamContent(_fileStream);", it works well even with 1 GB file. But, of course I cannot track the upload progress. I wonder if you have run into a similar issue, and if so, what is the solution? I have been searching, but have not found anything helpful.
Vahid NPosted Jun 29, 2021, 2:48 AM
Blazor 5x uses the [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) behind the scene and this API doesn't support what you have written here. this progress is just the percentage of the buffering a file inside of the browser. More info: https://stackoverflow.com/questions/35711724/upload-progress-indicators-for-fetch/35747208#35747208
Pranam BhatPosted Jun 28, 2021, 1:16 PM
Nice one! Very useful!!