12/9/2019 Admin

A Blazor Image File Manager


You can easily create an Image File Manager in Blazor when using the free Radzen Blazor Components .

image

Clicking the Upload Files button allows you to upload multiple images to the currently selected folder.

image

Selecting an image, and clicking the Delete File button will remove it from the file system.

image

To add a Folder, select the Add Folder option from the Folder button drop down.

image

This brings up the New Folder Name dialog.

Enter a name for the new Folder and click the Add Folder button.

image

The new Folder will be created.

image

The Delete Folder option will delete the currently selected folder, and all its sub-folders and files.

Creating The Application

image

Security is important when uploading files. Therefore we will start with the project from the article: A Simple Blazor User and Role Manager.

image

In addition, we will will want to leverage the power of the free Radzen Blazor Components (this is the page that will provide documentation on the controls we will implement in this article).

Follow the directions at this link to install the controls: https://razor.radzen.com/get-started

The Back End

image

First, we add a class file to the project called FileObject.cs with the following code:

								
									using
									System;
								
									using
									System.Collections.Generic;
								
									using
									System.Linq;
								
									using
									System.Threading.Tasks;
								
									namespace
									BlazorUsersRoles
								
{
								
									public
									class
									FileObject
								
									{
								
									public
									string
									FileName {
									get;
									set; }
								
									public
									string
									FilePath {
									get;
									set; }
								
									}
								
}
								

							

image

Next we add a controller class to the project, to handle file uploads, using the following code:

								
									using
									Microsoft.AspNetCore.Authorization;
								
									using
									Microsoft.AspNetCore.Hosting;
								
									using
									Microsoft.AspNetCore.Http;
								
									using
									Microsoft.AspNetCore.Mvc;
								
									using
									System;
								
									using
									System.IO;
								
									using
									System.Linq;
								
									using
									System.Threading.Tasks;
								
									namespace
									BlazorUsersRoles.Controllers
								
{
								
									[Route("api/[controller]")]
								
									[ApiController]
								
									// Must be in the Administrator Role
								
									[Authorize(Roles = "Administrators")]
								
									public
									class
									UploadController : Controller
								
									{
								
									private
									readonly
									IWebHostEnvironment environment;
								
									public
									UploadController(IWebHostEnvironment environment)
								
									{
								
									this.environment = environment;
								
									}
								
									[HttpPost("[action]")]
								
									public
									async Task<IActionResult> MultipleAsync(
								
									IFormFile[] files,
									string
									CurrentDirectory)
								
									{
								
									try
								
									{
								
									if
									(HttpContext.Request.Form.Files.Any())
								
									{
								
									foreach
									(var file
									in
									HttpContext.Request.Form.Files)
								
									{
								
									// reconstruct the path to ensure everything
									
								
									// goes to uploads directory
								
									string
									RequestedPath =
								
									CurrentDirectory.ToLower()
								
									.Replace(environment.WebRootPath.ToLower(), "");
								
									if
									(RequestedPath.Contains("\\uploads\\"))
								
									{
								
									RequestedPath =
								
									RequestedPath.Replace("\\uploads\\", "");
								
									}
								
									else
								
									{
								
									RequestedPath = "";
								
									}
								
									string
									path =
								
									Path.Combine(
								
									environment.WebRootPath,
								
									"uploads",
								
									RequestedPath,
								
									file.FileName);
								
									using
									(var stream =
								
									new
									FileStream(path, FileMode.Create))
								
									{
								
									await file.CopyToAsync(stream);
								
									}
								
									}
								
									}
								
									return
									StatusCode(200);
								
									}
								
									catch
									(Exception ex)
								
									{
								
									return
									StatusCode(500, ex.Message);
								
									}
								
									}
								
									}
								
}
								

							

The key thing to note about this class is that it only allows users who are Administrators to call the class.

Also, it reconstructs the path that is passed as a parameter, to ensure that it only operates on files in the intended directory called content.

The File Administration Page

image

Next, we add a .razor page to the project (also a link to it on the navigation menu), using the following code:

								
@page "/filemanager"
								
@using
									System.IO
								
@using
									Microsoft.AspNetCore.Authorization;
								
@using
									Microsoft.AspNetCore.Identity;
								
@using
									System.Collections;
								
@inject Microsoft.AspNetCore.Hosting.IWebHostEnvironment HostEnvironment
								
@inject AuthenticationStateProvider AuthenticationStateProvider
								
<h3>Image File Manager</h3>
								
<AuthorizeView>
								
									<Authorized>
								
									@if
									(@context.User.IsInRole(ADMINISTRATION_ROLE))
								
									{
								
								
									}
								
									else
								
									{
								
									<p>You're not signed
									in
									as
									a user
									in
									@ADMINISTRATION_ROLE.</p>
								
									}
								
									</Authorized>
								
									<NotAuthorized>
								
									<p>You're not logged
									in.</p>
								
									</NotAuthorized>
								
</AuthorizeView>
								
@code {
								
									[CascadingParameter]
								
									private
									Task<AuthenticationState> authenticationStateTask {
									get;
									set; }
								
									string
									ADMINISTRATION_ROLE = "Administrators";
								
									bool
									ShowPopup =
									false;
								
									bool
									ShowFolderPopup =
									false;
								
									int
									progress;
								
									string
									info;
								
									string
									CurrentDirectory = "";
								
									string
									CurrentRoot = "";
								
									string
									NewFolderName = "";
								
									FileObject SelectedFile =
									new
									FileObject();
								
									List<string> Directories =
									new
									List<string>();
								
									List<FileObject> Files =
									new
									List<FileObject>();
								
									Dictionary<DateTime,
									string> events =
									new
									Dictionary<DateTime,
									string>();
								
}
								

							

image

When we run the application without logging in as an Administrator, the security indicates we are not logged in.

image

If we log in as an Administrator (see the article at this link to set this up. The default Administration account is: Admin@BlazorHelpWebsite.com / Password#1)…

image

… we now get a blank page.

Show The File Tree

image

We will create a directory in the wwwroot directory called uploads and place some folders and images in it.

Next, we add the following markup to the page:

								
									<
									div
									class="row"
									>
								
									<
									div
									class="col-md-4"
									style="margin-bottom: 16px"
									>
								
									<
									RadzenTree
									Change="@LogChange"
									Data="@Directories"
								
									Expand="@LoadDirectory"
								
									Style="width: 100%; height: 300px"
									>
								
									<
									RadzenTreeLevel
									Text="@GetTextForNode"
								
									Template="@FileOrFolderTemplate"
									/>
								
									</
									RadzenTree
									>
								
									</
									div
									>
								
									<
									div
									class="col-md-8"
									style="margin-bottom: 16px"
									>
								
									<
									RadzenDataList
									WrapItems="true"
									AllowPaging="true"
								
									Data="@Files"
									TItem="FileObject"
									>
								
									<
									Template
									Context="file"
									>
								
									@if (file.FileName == SelectedFile.FileName)
								
									{
								
									<
									RadzenImage
									Path="@file.FilePath"
								
									Style="width:100px;
									
								
									padding:10px;
								
									background-color:darkgrey"
								
									Click="@((args) => SelectImage(file))"
									/>
								
									}
								
									else
								
									{
								
									<
									RadzenImage
									Path="@file.FilePath"
								
									Style="width: 100px;
									
								
									padding: 10px;
								
									background-color: white"
								
									Click="@((args) => SelectImage(file))"
									/>
								
									}
								
									</
									Template
									>
								
									</
									RadzenDataList
									>
								
									</
									div
									>
								
									</
									div
									>
								

							

We add the following C# code to the @code section:

								
									protected
									override
									void
									OnInitialized()
								
									{
								
									CurrentRoot =
								
									Path.Combine(HostEnvironment.WebRootPath, "uploads");
								
									CurrentDirectory = CurrentRoot;
								
									Directories.Add(CurrentDirectory);
								
									LoadFiles();
								
									}
								
									// Files
								
									void
									SelectImage(FileObject file)
								
									{
								
									if
									(SelectedFile.FileName == file.FileName)
								
									{
								
									SelectedFile =
									new
									FileObject();
								
									}
								
									else
								
									{
								
									SelectedFile = file;
								
									}
								
									}
								
									void
									LogChange(TreeEventArgs args)
								
									{
								
									CurrentDirectory = args.Value
									as
									string;
								
									LoadFiles();
								
									}
								
									string
									GetTextForNode(object
									data)
								
									{
								
									return
									Path.GetFileName((string)data);
								
									}
								
									RenderFragment<RadzenTreeItem>
								
									FileOrFolderTemplate = (context) => builder =>
								
									{
								
									string
									path = context.Value
									as
									string;
								
									bool
									isDirectory = Directory.Exists(path);
								
									builder.OpenComponent<RadzenIcon>(0);
								
									builder.AddAttribute(1,
								
									"Icon", isDirectory ? "folder" :
								
									"insert_drive_file");
								
									if
									(!isDirectory)
								
									{
								
									builder.AddAttribute(2,
								
									"Style",
								
									"margin-left: 24px");
								
									}
								
									builder.CloseComponent();
								
									builder.AddContent(3, context.Text);
								
									};
								
									void
									LoadDirectory(TreeExpandEventArgs args)
								
									{
								
									CurrentDirectory = args.Value
									as
									string;
								
									// Only get the folders not the files
								
									args.Children.Data =
								
									Directory.EnumerateFileSystemEntries(CurrentDirectory)
								
									.Where(x => !x.Contains("."));
								
									args.Children.Text = GetTextForNode;
								
									args.Children.HasChildren =
								
									(path) => Directory.Exists((string)path);
								
									args.Children.Template = FileOrFolderTemplate;
								
									}
								
									void
									LoadFiles()
								
									{
								
									Files =
									new
									List<FileObject>();
								
									var FileNames =
								
									Directory.EnumerateFileSystemEntries(CurrentDirectory)
								
									.Where(x => x.Contains("."));
								
									foreach
									(var item
									in
									FileNames)
								
									{
								
									Files.Add(new
									FileObject()
								
									{
								
									FileName =
								
									Path.GetFileName(item),
								
									FilePath =
								
									item.Replace(HostEnvironment.WebRootPath, "")
								
									});
								
									}
								
									// Reset selected file
								
									SelectedFile =
									new
									FileObject();
								
									// Update UI
								
									StateHasChanged();
								
									}
								

								

								

							

								image
							

When we run the application, we will now have a Tree that will show the file folders. Clicking on a folder icon will display any images in that folder.

In addition, we can click on an image to select it.

Uploading Files

To allow images to be uploaded, we add an upload button using the following markup:

								
									<
									RadzenUpload
									ChooseText="Upload Files"
									Multiple="true"
								
									Accept="image/*"
								
									Url=@($"/api/upload/multiple?CurrentDirectory={CurrentDirectory}")
								
									Style="margin-bottom: 20px;height: 45px"
								
									Progress="@((args) => OnProgress(args))"
									/>
								

							

Also a popup to show the uploading progress:

								
@if (ShowPopup)
								
{
								
									<
									div
									class="modal"
									tabindex="-1"
									style="display:block"
									role="dialog"
									>
								
									<
									div
									class="modal-dialog modal-sm"
									>
								
									<
									div
									class="modal-content"
									>
								
									<
									div
									class="modal-header"
									>
								
									<
									h4
									class="modal-title"
									>Upload Status</
									h4
									>
								
									</
									div
									>
								
									<
									div
									align="center"
									>
								
									<
									RadzenProgressBar
									Value="@progress"
								
									Unit="@info"
								
									Visible="@(progress > 0)"
								
									Style="margin-bottom: 20px"
									/>
								
									</
									div
									>
								
									</
									div
									>
								
									</
									div
									>
								
									</
									div
									>
								
}
								

							

We then add the following to the @code section:

								
									async
									void
									OnProgress(UploadProgressArgs args)
								
									{
								
									ShowPopup =
									true;
								
									this.info = $"{args.Loaded} of {args.Total} bytes.";
								
									this.progress = args.Progress;
								
									StateHasChanged();
								
									if
									(args.Loaded == args.Total)
								
									{
								
									// Delay to give files time to be saved
								
									// before reloading file view
								
									await LoadFilesAsync();
								
									}
								
									}
								
									public
									async Task LoadFilesAsync()
								
									{
								
									int
									Time = 1;
								
									while
									(Time > 0)
								
									{
								
									Time--;
								
									StateHasChanged();
								
									await Task.Delay(1000);
								
									}
								
									ShowPopup =
									false;
								
									LoadFiles();
								
									}
								

							

image

Now we can select a folder, then click the Upload Files button…

image

select files from our computer…

image

…and the files will be uploaded to the directory.

Deleting The Selected File

We already have code that allows us to determine the selected file.

To allow that file to be deleted , we add the following markup for the button (note, the delete button will be disabled if a file is not selected):

								
									<
									RadzenButton
									Disabled="@(SelectedFile.FileName == null)"
								
									Text="Delete File"
								
									Click="DeleteSelectedFile"
								
									ButtonStyle="ButtonStyle.Danger"
								
									Style="margin-bottom: 20px;height: 35px"
									/>
								

								

								

							

Next, we add the following code to delete the file:

								
									void
									DeleteSelectedFile()
								
{
								
									string
									RequestedPath = SelectedFile.FilePath;
								
									RequestedPath =
								
									RequestedPath.Replace("\\uploads\\", "uploads\\");
								
									string
									path = Path.Combine(
								
									HostEnvironment.WebRootPath,
								
									RequestedPath);
								
									if
									(File.Exists(path))
								
									{
								
									File.Delete(path);
								
									LoadFiles();
								
									}
								
}
								

							

image

When we run the application, we can select a file, then click the Delete File button to delete it.

Folder Management

Finally, we will implement code to allow us to create folders and to delete folders (and the sub folders and files contained inside them).

First, add the following Split Button to the markup:

								
									<
									RadzenSplitButton
									Click="(args) => FolderAction(args)"
								
									Text="Folder"
								
									ButtonStyle="ButtonStyle.Info"
								
									Style="margin-bottom: 20px;height: 35px"
									>
								
									<
									ChildContent
									>
								
									<
									RadzenSplitButtonItem
									Text="Add Folder"
									Value="Add"
									/>
								
									<
									RadzenSplitButtonItem
									Text="Delete Folder"
									Value="Delete"
									/>
								
									</
									ChildContent
									>
								
									</
									RadzenSplitButton
									>
								

							

Next add the following popup:

								
@if (ShowFolderPopup)
								
{
								
									<
									div
									class="modal"
									tabindex="-1"
									style="display:block"
									role="dialog"
									>
								
									<
									div
									class="modal-dialog modal-sm"
									>
								
									<
									div
									class="modal-content"
									>
								
									<
									div
									class="modal-header"
									>
								
									<
									h4
									class="modal-title"
									>New Folder Name</
									h4
									>
								
									<!-- Button to close the popup -->
								
									<
									button
									type="button"
									class="close"
								
									@onclick="CloseFolderPopup"
									>
								
									<
									span
									aria-hidden="true"
									>X</
									span
									>
								
									</
									button
									>
								
									</
									div
									>
								
									<
									div
									align="center"
									>
								
									<
									RadzenTextBox
									Placeholder="Folder Name"
								
									@bind-Value="NewFolderName"
								
									Style="margin-bottom: 20px"
									/>
								
									<
									RadzenButton
									Text="Add Folder"
								
									Click="AddFolderName"
								
									ButtonStyle="ButtonStyle.Success"
								
									Style="margin-bottom: 20px;height: 35px"
									/>
								
									</
									div
									>
								
									</
									div
									>
								
									</
									div
									>
								
									</
									div
									>
								
}
								

							

Lastly, add the following to the @code section:

								
									void
									AddFolder()
								
									{
								
									ShowFolderPopup =
									true;
								
									}
								
									void
									CloseFolderPopup()
								
									{
								
									ShowFolderPopup =
									false;
								
									}
								
									void
									AddFolderName()
								
									{
								
									string
									path = Path.Combine(
								
									HostEnvironment.WebRootPath,
								
									CurrentDirectory,
								
									NewFolderName);
								
									if
									(!Directory.Exists(path))
								
									{
								
									Directory.CreateDirectory(path);
								
									LoadFiles();
								
									}
								
									NewFolderName = "";
								
									ShowFolderPopup =
									false;
								
									}
								
									void
									FolderAction(RadzenSplitButtonItem item)
								
									{
								
									if
									(item !=
									null)
								
									{
								
									if
									(item.Value == "Add")
								
									{
								
									ShowFolderPopup =
									true;
								
									}
								
									// Delete
								
									if
									(item.Value == "Delete")
								
									{
								
									if
									(CurrentDirectory != CurrentRoot)
								
									{
								
									string
									path = Path.Combine(
								
									HostEnvironment.WebRootPath,
								
									CurrentDirectory);
								
									if
									(Directory.Exists(path))
								
									{
								
									Directory.Delete(path,
									true);
								
									CurrentDirectory =
								
									HostEnvironment.WebRootPath;
								
									LoadFiles();
								
									}
								
									}
								
									}
								
									}
								
									}
								

								

								

							

								image
							

The application is now complete.

Note

When publishing the application, for example to Microsoft Azure, ensure you have a directory created under the wwwroot\wwwroot directory called uploads or the application will not work.

For documentation on the controls covered in this article, see this page: Radzen Blazor Components .

Links

Radzen

Blazor.net

Download

The project is available on the Downloads page on this site.

You must have Visual Studio 2019 (or higher) installed to run the code.

An error has occurred. This application may no longer respond until reloaded. Reload 🗙