Blazor is truly a great tool, greatly improving development efficiency. Many page interaction features can be implemented with very little code, and without JavaScript. You would never have imagined how simple file uploads can be with Blazor!
Let's start with the conclusion: Implementing file uploads with progress display in Blazor is really simple! Check out the effect in the image:

This small feature was implemented in less than 50 lines of code. Now let me share the implementation with you.
First, introduce the Tewr.Blazor.FileReader package. This package provides streaming reading of file uploads, allowing you to write the file on the server side as it is being uploaded.
Configure dependency injection (Editor's note: This is for Blazor Server mode; for WASM mode, please refer to the repository documentation at the end of the article):
services.AddFileReaderService();
Next, let's lay out the page. It's very simple, with two variables declared to show progress and display the image:
<input type="file" /><button>Upload file</button>
<div>
@if (!string.IsNullOrEmpty(_src)) {
<img src="@_src" width="600px" />
} else {
<p>@progress</p>
}
</div>
Then inject the IFileReaderService service into the component:
@using Tewr.Blazor.FileReader @inject IFileReaderService fileReaderService;
To allow the file input to interact with C# code, reference it via ElementReference:
<input @ref="inputTypeFileElement" type="file" /><button>Upload file</button>
<div>
@if (!string.IsNullOrEmpty(_src)) {
<img src="@_src" width="600px" />
} else {
<p>@progress</p>
}
</div>
@code { private ElementReference inputTypeFileElement; private string _src;
private string progress; }
Bind an event to the button. When the button is triggered, read the file stream via fileReaderService. Then perform the usual binary data copy operation, which allows you to get the file transfer progress, calculate it, and display it on the page:
<button @onclick="ReadFile">Upload file</button>
public async Task ReadFile()
{
_src = "";
foreach (var file in await fileReaderService.CreateReference(inputTypeFileElement).EnumerateFilesAsync())
{
await using var fileStream = await file.OpenReadAsync();
var buffer = new byte[2048];
var finalBuffer = new byte[fileStream.Length];
int count;
int totalCount = 0;
while ((count = await fileStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
{
Buffer.BlockCopy(buffer, 0, finalBuffer, totalCount, count);
totalCount += count;
progress = "File uploading " + (int)(totalCount * 100.0 / fileStream.Length) + "%";
StateHasChanged();
}
_src = $"data:image/jpg;base64,{Convert.ToBase64String(finalBuffer)}";
progress = "";
StateHasChanged();
}
}
Complete code:
@page "/counter" @using Tewr.Blazor.FileReader @inject IFileReaderService
fileReaderService;
<input @ref="inputTypeFileElement" type="file" />
<button @onclick="ReadFile">Upload file</button>
<div>
@if (!string.IsNullOrEmpty(_src)) {
<img src="@_src" width="600px" />
} else {
<p>@progress</p>
}
</div>
@code { private ElementReference inputTypeFileElement; private string _src;
private string progress; public async Task ReadFile() { _src = ""; foreach (var
file in await
fileReaderService.CreateReference(inputTypeFileElement).EnumerateFilesAsync()) {
await using var fileStream = await file.OpenReadAsync(); var buffer = new
byte[2048]; var finalBuffer = new byte[fileStream.Length]; int count; int
totalCount = 0; while ((count = await fileStream.ReadAsync(buffer, 0,
buffer.Length)) != 0) { Buffer.BlockCopy(buffer, 0, finalBuffer, totalCount,
count); totalCount += count; progress = "File uploading " + (int)(totalCount * 100.0
/ fileStream.Length) + "%"; StateHasChanged(); } _src =
$"data:image/jpg;base64,{Convert.ToBase64String(finalBuffer)}"; progress = "";
StateHasChanged(); } } }
Editor's interjection:
The image at the beginning of the article demonstrates uploading a file under 1MB. Since the Tewr.Blazor.FileReader package provides streaming reading, uploading large files is also possible. Below is an example of uploading a 34.2MB ZIP archive using Blazor Server mode:

The demo is simple, and the gif might not show much, but it proves this package is really good. To implement large file uploads, you can increase the single read chunk size, for example to 512KB:
var buffer = new byte[1024*512];
If you look at Microsoft's Blazor file upload documentation below, when you set the chunk size larger than 20KB, the page might freeze for a moment and then automatically refresh, resetting the upload operation. However, this package does not have that issue—it's very nice.
OK, that's all for this article.
References