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 🗙