In most websites, image uploading is also a very important feature. Today we'll walk through it.
(Note: This is done using Blazor Server, but it's best not to upload too many files. So if you try to upload more than 4 photos, there will be a prompt. After all, these operations are performed on the server, which can be a heavy burden. Microsoft also recommends using .NET Core Web API for this.)
First, we create a Component FileUpload.
The code below is for FileUpload.razor, which uses Blazor's built-in component <InputFile>. The multiple attribute allows uploading multiple files.
@page "/FileUpload"
<div>
<div>
<InputFile OnChange="OnChange" multiple></InputFile>
</div>
<div>
<MyButton value="Submit" class="btn btn-primary" type="submit" @onclick="OnSubmit"></MyButton>
</div>
</div>
@if (ImageList.Count > 0)
{
<table>
<tr>
@foreach (var img in ImageList)
{
<td>
<img src="@img" width="150" height="150"/>
</td>
}
</tr>
</table>
}
The code below is for FileUpload.razor.cs, using a partial class.
using BlazorServer.ViewModels;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.JSInterop;
using System.Text.Json;
namespace BlazorServer.Shared;
public partial class FileUpload
{
private JsInteropClasses? _jsClass;
// Get the file content from `<InputFile>`
public IReadOnlyList<IBrowserFile>? ImageFiles;
public List<string> ImageList = new();
public string? ImageSrc;
[Inject] protected IJSRuntime Js { get; set; }
/// <summary>
/// Used to determine the execution environment at runtime
/// </summary>
[Inject]
protected IWebHostEnvironment? Env { get; set; }
protected override Task OnInitializedAsync()
{
_jsClass = new JsInteropClasses(Js);
return base.OnInitializedAsync();
}
public async Task OnChange(InputFileChangeEventArgs e)
{
ImageList = new List<string>();
const string format = "image/jpeg";
// Get the files
ImageFiles = e.GetMultipleFiles();
foreach (var file in ImageFiles)
{
// Convert the image content to the specified format and maximum size
var imageFile = await file.RequestImageFileAsync(format, 1200, 675);
// Read the image content using a Stream
await using var fileStream = imageFile.OpenReadStream();
// Read the Stream into memory; do not do this if you are not sure about uploading, to avoid wasting memory
await using var memoryStream = new MemoryStream();
await fileStream.CopyToAsync(memoryStream);
ImageSrc = $"data:{format};base64,{Convert.ToBase64String(memoryStream.ToArray())}";
// Display the image using a Data URI
ImageList.Add(ImageSrc);
}
}
public async Task OnSubmit()
{
// Convert the prompt message into a ViewModel
var sweetConfirm = new SweetConfirmViewModel
{
RequestTitle = "Are you sure you want to upload the image?",
ResponseTitle = "Upload successful"
};
var jsonString = JsonSerializer.Serialize(sweetConfirm);
var result = await _jsClass!.Confirm(jsonString);
if (result && ImageFiles != null && ImageFiles.Any())
{
long maxFileSize = 1024 * 1024 * 15;
// Specify the image storage path
var folder = $@"{Env!.WebRootPath}\images";
foreach (var file in ImageFiles)
{
// Use a Stream to store the file to the specified path
await using var stream = file.OpenReadStream(maxFileSize);
// Create the folder if it doesn't exist
Directory.CreateDirectory(folder);
var path = $@"{Env.WebRootPath}\images\{file.Name}";
// Create the file
var fs = File.Create(path);
// Copy the image Stream to the file
await stream.CopyToAsync(fs);
// Remember to close the Stream after use
stream.Close();
fs.Close();
}
}
}
}
For convenience, add a route to navigate to this Component in NavMenu.razor.
<div class="nav-item px-3">
<NavLink class="nav-link" href="FileUpload" Match="NavLinkMatch.All">
<span class="bi bi-card-image h4 p-2 mb-0" aria-hidden="true"></span> File
Upload
</NavLink>
</div>
Create a new ViewModel so that SweetConfirm can be reused.
namespace BlazorServer.ViewModels;
public class SweetConfirmViewModel
{
public string? ResponseTitle { get; set; }
public string? ResponseText { get; set; }
public string? RequestTitle { get; set; }
public string? RequestText { get; set; }
}
Then modify the SweetConfirm in _Layout.cshtml.
function SweetConfirm(jsonString) {
// Need to parse here so it can be properly passed back
var arg = JSON.parse(jsonString);
return new Promise((resolve) => {
swal({
title: arg.RequestTitle,
text: arg.RequestText,
icon: "warning",
buttons: ["Cancel", "OK"],
dangerMode: true
}).then((willDelete) => {
resolve(willDelete);
if (willDelete) {
swal(arg.ResponseTitle, arg.ResponseText, "success");
}
});
});
}
Since this has been changed, DeletePost in PostBase.razor.cs also needs to be modified.
protected async Task DeletePost()
{
// Changed to ViewModel
var sweetConfirm = new SweetConfirmViewModel
{
RequestTitle = $"Are you sure you want to delete the post {Post!.Title}?",
RequestText = "This operation cannot be undone",
ResponseTitle = "Delete successful",
ResponseText = "The post has been deleted"
};
var jsonString = JsonSerializer.Serialize(sweetConfirm);
var result = await _jsClass.Confirm(jsonString);
if (result)
{
var deleted = await PostRepository!.DeletePost(Post.Id);
if (deleted.IsSuccess)
await GetPostId.InvokeAsync(Post!.Id);
else
await _jsClass.Alert(deleted.Message!);
}
}
Change Confirm() in JsInteropClasses.cs to accept a JSON string.
public async ValueTask<bool> Confirm(string jsonString)
{
var confirm = await _js.InvokeAsync<object?>("SweetConfirm", jsonString);
if (confirm == null)
return false;
return bool.TryParse(confirm.ToString(), out var result) && result;
}
You can see the image upload was successful.

References:
- ASP.NET Core Blazor file uploads
- Upload Files Using InputFile Component In Blazor
- What scope does a using statement have without curly braces
- BrowserFileExtensions.RequestImageFileAsync(IBrowserFile, String, Int32, Int32) Method
- Day 26:Blazor WebAssembly Upload Files
Note: The code in this article has been refactored using .NET 6 + Visual Studio 2022. You can click the original article link to compare with the refactored code. Thank you for reading and supporting the original author.