12/15/2019 Admin

Uploading Images With The Blazor Rich Text Editor


By combining Blazor components, you can easily enable uploading of images into the Blazor Rich Text Editor.

 

Blazor-Blogs

image

We start with the Blazor Blogs Github project at: https://github.com/ADefWebserver/Blazor-Blogs.

 

image

We will want to leverage the power of the free Radzen Blazor Components, so we follow the directions at this link to install the controls: https://razor.radzen.com/get-started.

 

image

We will also need to install the NuGet package System.Drawing.Common to allow us to properly resize thumbnail images.

 

Uploading Files Using A File Manager

image

The article: A Blazor Image File Manager provides a starting point for implementing a File Manager that allows us to upload images.

 

image

The first step is to add a UploadController.cs class to the project 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 BlazorBlogs
{
    public class FileObject
    {
        public string FileName { get; set; }
        public string FilePath { get; set; }
        public int Height { get; set; }
        public int Width { get; set; }
        public int ThumbnailHeight { get; set; }
        public int ThumbnailWidth { get; set; }
    }
    [Route("api/[controller]")]
    [ApiController]
    // to ensure that user 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);
            }
        }
    }
}

 

image

Next, we add a FileManager.razor control using the following code:

 

@using System.IO
@using System.Drawing;
@using Radzen
@using Radzen.Blazor
@inject Microsoft.AspNetCore.Hosting.IWebHostEnvironment HostEnvironment
@if (ShowFileManager)
{
    <div class="modal" tabindex="-1" style="display:block" role="dialog">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <b class="modal-title">Insert Image</b>
                    <!-- Close Button -->
                    <button type="button" class="close"
                            @onclick="() => { ShowFileManager = false; }">
                        <span aria-hidden="true">X</span>
                    </button>
                </div>
                <div class="modal-body">
                    <div class="row">
                        <RadzenUpload ChooseText="Upload Files" Multiple="true"
                                      Accept="image/*"
                                      Url=@($"/api/upload/multiple?CurrentDirectory={CurrentDirectory}")
                                      Style="margin-bottom: 20px;height: 45px"
                                      Progress="@((args) => OnProgress(args))" />
                        <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>
                        <RadzenButton Disabled="@(SelectedFile.FileName == null)"
                                      Text="Delete"
                                      Click="DeleteSelectedFile"
                                      ButtonStyle="ButtonStyle.Danger"
                                      Style="margin-bottom: 20px;height: 35px" />
                        <RadzenButton Disabled="@(SelectedFile.FileName == null)"
                                      Text="Select"
                                      Click="SelectFile"
                                      ButtonStyle="ButtonStyle.Success"
                                      Style="margin-bottom: 20px;height: 35px" />
                    </div>
                    <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 @ref="FileDataList" Data="@Files"
                                            AllowPaging="true" TItem="FileObject"
                                            WrapItems="true" PageSize="6">
                                <Template Context="file">
                                    <RadzenImage Path="@file.FilePath"
                                                 Style="@((file.FileName == SelectedFile.FileName)
                                                          ? $"height:{file.ThumbnailHeight}px;" +
                                                          $"width:{file.ThumbnailWidth}px;" +
                                                          $"padding:10px;background-color:darkgrey;"
                                                          : $"height:{file.ThumbnailHeight}px;" +
                                                          $"width:{file.ThumbnailWidth}px;" +
                                                          $"padding:10px;background-color:white;")"
                                                 Click="@((args) => SelectImage(file))" />
                                </Template>
                            </RadzenDataList>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
}
@if (ShowProgressBarPopup)
{
    <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>
}
@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 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>
}
@code {
    // ImageSelected is an EventCallback that will
    // notify the parent component when an image is selected
    // passing the url of the image to the parent component
    [Parameter] public EventCallback<string> ImageSelected { get; set; }
    private RadzenDataList<FileObject> FileDataList;
    private bool ShowFileManager = false;
    private bool ShowProgressBarPopup = false;
    private bool ShowFolderPopup = false;
    private int progress;
    private string info;
    private string CurrentDirectory = "";
    private string CurrentRoot = "";
    private string NewFolderName = "";
    private FileObject SelectedFile = new FileObject();
    private List<string> Directories = new List<string>();
    private List<FileObject> Files = new List<FileObject>();
    private Dictionary<DateTime, string> events =
        new Dictionary<DateTime, string>();
    protected override void OnInitialized()
    {
        CurrentRoot =
            Path.Combine(HostEnvironment.WebRootPath, "uploads");
        CurrentDirectory = CurrentRoot;
        Directories.Add(CurrentDirectory);
        LoadFiles();
    }
    public void SetShowFileManager(bool paramSetting)
    {
        ShowFileManager = paramSetting;
    }
    //SelectFile
    private async Task SelectFile()
    {
        await ImageSelected.InvokeAsync(SelectedFile.FilePath);
    }
    // Files
    private void SelectImage(FileObject file)
    {
        if (SelectedFile.FileName == file.FileName)
        {
            SelectedFile = new FileObject();
        }
        else
        {
            SelectedFile = file;
        }
    }
    private void LogChange(TreeEventArgs args)
    {
        // Get the current directory from the
        // argument passed to the method
        CurrentDirectory = args.Value as string;
        // Set the RadzenDataList to page 1
        FileDataList.FirstPage();
        // Reload the FileDataList
        LoadFiles();
    }
    private string GetTextForNode(object data)
    {
        return Path.GetFileName((string)data);
    }
    private 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);
        };
    private 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;
    }
    private void LoadFiles()
    {
        Files = new List<FileObject>();
        var FileNames =
            Directory.EnumerateFileSystemEntries(CurrentDirectory)
            .Where(x => x.Contains("."));
        foreach (var item in FileNames)
        {
            using (var image = Image.FromFile(item))
            {
                // Calculate Thumbnail
                int thumbnailHeight = 100;
                int thumbnailWidth = 100;
                double x = image.Height / 100;
                if (x > 0)
                {
                    thumbnailHeight = Convert.ToInt32(image.Height / x);
                    thumbnailWidth = Convert.ToInt32(image.Width / x);
                }
                Files.Add(new FileObject()
                {
                    FileName =
                    Path.GetFileName(item),
                    FilePath =
                    item.Replace(HostEnvironment.WebRootPath, ""),
                    Height = image.Height,
                    Width = image.Width,
                    ThumbnailHeight = Convert.ToInt32(thumbnailHeight),
                    ThumbnailWidth = Convert.ToInt32(thumbnailWidth)
                });
            }
        }
        // Reset selected file
        SelectedFile = new FileObject();
        // Update UI
        StateHasChanged();
    }
    // Uploading
    private async void OnProgress(UploadProgressArgs args)
    {
        ShowProgressBarPopup = 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();
        }
    }
    private async Task LoadFilesAsync()
    {
        int Time = 1;
        while (Time > 0)
        {
            Time--;
            StateHasChanged();
            await Task.Delay(1000);
        }
        ShowProgressBarPopup = false;
        LoadFiles();
    }
    // Deleteing
    private 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();
        }
    }
    // Folders
    private void AddFolder()
    {
        ShowFolderPopup = true;
    }
    private void CloseFolderPopup()
    {
        ShowFolderPopup = false;
    }
    private void AddFolderName()
    {
        string path = Path.Combine(
            HostEnvironment.WebRootPath,
            CurrentDirectory,
            NewFolderName);
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
            LoadFiles();
        }
        NewFolderName = "";
        ShowFolderPopup = false;
    }
    private 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

To allow the File Manager to be opened, we first add a image icon to the project.

 

image

Then we open the BlogAdministration.razor control and add the FileManagerControl:

 

    <FileManager @ref="FileManagerControl"
                    ImageSelected="InsertImage" />

 

This also requires a backing property to be added to the @code section:

 

    private FileManager FileManagerControl;

 

Also, we need code to respond to the InsertImage event, to insert the selected image into the Rich Text Editor:

 

    async Task InsertImage(string paramImageURL)
    {
        await this.QuillHtml.InsertImage(paramImageURL);
        FileManagerControl.SetShowFileManager(false);
    }

 

We also add the following button to the toolbar of the Rich Text Editor:

 

            <button class="btn btn-link" id="custom-button" 
                    @onclick="InsertImageClick">
            <img src="imageIcon.png" style="border:hidden" />
            </button>

 

We add a method to show the File Manager when that button is clicked:

 

    private void InsertImageClick()
    {
        FileManagerControl.SetShowFileManager(true);
    }

 

Finally, the File Manager code expects an uploads directory, so we add that:

 

image

Inserting Images

image

At this point, we can run the application, log in as an Administrator, navigate to the Blog Administration page, and Add or Edit a Blog post.

 

image

We can click on the image icon, on the Rich Text Editor, to open the File Manager.

 

image

The File Manager allows us to create and edit Folders.

 

image

The Upload Files button allows us to upload images into the Folders.

 

image

We can then select any uploaded image, and then click the Select button…

 

image

… to insert the image into the editor.

Clicking on a image allows the image to be resized.

 

 

Links

Blazor-Blogs (GitHub)

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