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 🗙