From ce4b1772da1b2bb4e19d44d2d856024bf5ce57d7 Mon Sep 17 00:00:00 2001 From: Dimo Dimov <961014+dimodi@users.noreply.github.com> Date: Thu, 13 Mar 2025 14:59:50 +0200 Subject: [PATCH] docs(FileManager): Revamp Upload example --- components/filemanager/upload.md | 415 +++++++++++++------------ components/upload/events.md | 4 +- components/upload/overview.md | 4 +- knowledge-base/upload-preview-image.md | 4 +- 4 files changed, 221 insertions(+), 206 deletions(-) diff --git a/components/filemanager/upload.md b/components/filemanager/upload.md index bf09941b12..337b21f5b6 100644 --- a/components/filemanager/upload.md +++ b/components/filemanager/upload.md @@ -12,7 +12,7 @@ position: 17 The FileManager allows uploading of files through an integrated Upload component. The Upload shows in a dialog when the user clicks on the [`Upload` Toolbar button](slug:filemanager-toolbar). ->tip Before continuing, make sure you are familiar with the features and API of the [Upload component](slug:upload-overview). +>tip Before you continue, make sure you are familiar with the features and API of the [Telerik Upload component](slug:upload-overview). Configure the integrated Upload through the `FileManagerUploadSettings` child tag of `FileManagerSettings`. The FileManager exposes parameter names that are identical to the respective Upload component API members: @@ -22,11 +22,13 @@ Configure the integrated Upload through the `FileManagerUploadSettings` child ta ## Example -The example below demonstrates how to handle successful upload on the FileManager. Note the following milestones: +The example below demonstrates how to handle successful uploads in the FileManager. Note the following milestones: -* To actually **upload** the file, [implement a controller method](slug:upload-overview#implement-controller-methods). -* To save the file in the **correct folder**, use the [Upload's `OnUpload` event](slug:upload-events#onupload) and send the FileManager's `Path` value to the controller. -* To receive an optional **custom response** from the controller, use the [Upload's `OnSuccess` event arguments](slug:upload-events#onsuccess). +* To upload the file, [implement a controller method](slug:upload-overview#implement-controller-methods). +* To save the file to the correct folder, use the [Upload's `OnUpload` event](slug:upload-events#onupload) and send the FileManager's `Path` value to the controller. +* To receive an optional custom response from the controller, use the [Upload's `OnSuccess` event arguments](slug:upload-events#onsuccess). + +The `FileManagerController` class below assumes that the project name and namespace is `TelerikBlazorApp`. The FileManager `Data` is the contents of the application's `wwwroot` folder. >caption Using FileManager Upload @@ -35,246 +37,160 @@ The example below demonstrates how to handle successful upload on the FileManage ````RAZOR @using System.IO +@inject NavigationManager NavigationManager + +

Path: @FileManagerPath

+ + @bind-Path="@FileManagerPath"> - - + @code { - private List FileManagerData = new List(); + private List FileManagerData { get; set; } = new(); - private string RootPath { get; set; } = "root-folder-path"; - private string DirectoryPath { get; set; } = "root-folder-path"; + private string FileManagerPath { get; set; } = string.Empty; - private async Task OnUploadUpload(UploadEventArgs args) + // The source root folder for FileManagerData + private readonly string RootPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot"); + + private string ToAbsoluteUrl(string url) { - // Send the correct save location to the Upload controller - args.RequestData.Add("FileManagerPath", DirectoryPath); + return string.Concat(NavigationManager.BaseUri, url); } - private async Task OnUploadSuccess(UploadSuccessEventArgs args) + private void OnFileManagerUploadRequest(UploadEventArgs args) { - await UploadAsync(args, DirectoryPath); + // If FileManagerPath is empty, the controller action will not be hit + string pathToSend = string.IsNullOrEmpty(FileManagerPath) ? "/" : FileManagerPath; + + args.RequestData.Add("FileManagerPath", pathToSend); } - private async Task UploadAsync(UploadSuccessEventArgs args, string path) + private async Task OnFileManagerUploadSuccess(UploadSuccessEventArgs args) { - await Task.Delay(300); + FileManagerData = await GetFileManagerData(); - var uploadFiles = args.Files; + // OR + // you can add or remove items in FileManagerData manually + // depending on the value of args.Operation and args.Files + } + + protected override async Task OnInitializedAsync() + { + FileManagerData = await GetFileManagerData(); + } - // Optional: use information from the Upload controller response - //var uploadResponse = args.Request.ResponseText; + #region FileManager Data Generation - var files = uploadFiles - .Select(x => new FlatFileEntry() - { - Name = Path.GetFileNameWithoutExtension(x.Name), - IsDirectory = false, - HasDirectories = false, - DateCreated = DateTime.Now, - DateCreatedUtc = DateTime.Now, - DateModified = DateTime.Now, - DateModifiedUtc = DateTime.Now, - Path = Path.Combine(path, x.Name), - Extension = Path.GetExtension(x.Name), - Size = x.Size - }) - .ToList(); - - var directory = GetDirectory(path); - var directoryItems = FileManagerData; - - for (int i = 0; i < files.Count; i++) - { - var file = files[i]; - directoryItems.Add(file); - } + private async Task> GetFileManagerData() + { + // Simulate async operation + await Task.CompletedTask; - RefreshData(); + return ReadFileSystem(); } - private FlatFileEntry GetDirectory(string path) + private List ReadFileSystem() { - var directory = FileManagerData.FirstOrDefault(x => x.IsDirectory && x.Path == path); + var items = new List(); - return directory; + string rootPath = Path.Combine(RootPath); + DirectoryInfo rootDirectory = new(rootPath); + + AddChildren(items, rootDirectory, null); + + return items; } - private void RefreshData() + private void AddDirectory(List items, DirectoryInfo directoryInfo, string? parentId) { - FileManagerData = new List(FileManagerData); + FileManagerItem directoryEntry = ConvertToFileManagerItem(directoryInfo, parentId); + items.Add(directoryEntry); + + AddChildren(items, directoryInfo, directoryEntry.Id); } - protected override async Task OnInitializedAsync() + private void AddChildren(List items, DirectoryInfo directoryInfo, string? directoryId) { - FileManagerData = await GetFlatFileEntries(); + IEnumerable files = directoryInfo.EnumerateFiles(); + foreach (FileInfo file in files) + { + FileManagerItem item = ConvertToFileManagerItem(file, directoryId); + items.Add(item); + } + + IEnumerable directories = directoryInfo.EnumerateDirectories(); + foreach (DirectoryInfo directory in directories) + { + AddDirectory(items, directory, directoryId); + } } - //initialize the model to allow new folder creation - private FlatFileEntry OnModelInitHandler() + private FileManagerItem ConvertToFileManagerItem(DirectoryInfo directory, string? parentId) { - var item = new FlatFileEntry(); - item.Name = $"New folder"; - item.Size = 0; - item.Path = Path.Combine(DirectoryPath, item.Name); - item.IsDirectory = true; - item.HasDirectories = false; - item.DateCreated = DateTime.Now; - item.DateCreatedUtc = DateTime.Now; - item.DateModified = DateTime.Now; - item.DateModifiedUtc = DateTime.Now; + var item = new FileManagerItem() + { + ParentId = parentId, + Name = directory.Name, + IsDirectory = true, + HasDirectories = directory.GetDirectories().Count() > 0, + DateCreated = directory.CreationTime, + DateCreatedUtc = directory.CreationTimeUtc, + DateModified = directory.LastWriteTime, + DateModifiedUtc = directory.LastWriteTimeUtc, + // Trim the path to avoid exposing it + Path = directory.FullName.Substring(directory.FullName.IndexOf(RootPath) + RootPath.Length), + Extension = directory.Extension, + // Hard-coded for simplicity, otherwise requires recursion + Size = 2 * 1024 * directory.GetFiles().LongCount() + }; return item; } - // The FileManager is hard-coded, so you can explore the component more easily - private async Task> GetFlatFileEntries() + private FileManagerItem ConvertToFileManagerItem(FileInfo file, string? parentId) { + var item = new FileManagerItem() + { + ParentId = parentId, + Name = Path.GetFileNameWithoutExtension(file.FullName), + IsDirectory = false, + HasDirectories = false, + DateCreated = file.CreationTime, + DateCreatedUtc = file.CreationTimeUtc, + DateModified = file.LastWriteTime, + DateModifiedUtc = file.LastWriteTimeUtc, + // Trim the path to avoid exposing it + Path = file.FullName.Substring(file.FullName.IndexOf(RootPath) + RootPath.Length), + Extension = file.Extension, + Size = file.Length + }; - var workFiles = new FlatFileEntry() - { - Id = "1", - ParentId = null, - Name = "Work Files", - IsDirectory = true, - HasDirectories = true, - DateCreated = new DateTime(2022, 1, 2), - DateCreatedUtc = new DateTime(2022, 1, 2), - DateModified = new DateTime(2022, 2, 3), - DateModifiedUtc = new DateTime(2022, 2, 3), - Path = Path.Combine(RootPath, "Work Files"), - Size = 3 * 1024 * 1024 - }; - - var Documents = new FlatFileEntry() - { - Id = "2", - ParentId = workFiles.Id, - Name = "Documents", - IsDirectory = true, - HasDirectories = false, - DateCreated = new DateTime(2022, 1, 2), - DateCreatedUtc = new DateTime(2022, 1, 2), - DateModified = new DateTime(2022, 2, 3), - DateModifiedUtc = new DateTime(2022, 2, 3), - Path = Path.Combine(workFiles.Path, "Documents"), - Size = 1024 * 1024 - }; - - var Images = new FlatFileEntry() - { - Id = "3", - ParentId = workFiles.Id, - Name = "Images", - IsDirectory = true, - HasDirectories = false, - DateCreated = new DateTime(2022, 1, 2), - DateCreatedUtc = new DateTime(2022, 1, 2), - DateModified = new DateTime(2022, 2, 3), - DateModifiedUtc = new DateTime(2022, 2, 3), - Path = Path.Combine(workFiles.Path, "Images"), - Size = 2 * 1024 * 1024 - }; - - var specification = new FlatFileEntry() - { - Id = "4", - ParentId = Documents.Id, - Name = "Specification", - IsDirectory = false, - HasDirectories = false, - Extension = ".docx", - DateCreated = new DateTime(2022, 1, 5), - DateCreatedUtc = new DateTime(2022, 1, 5), - DateModified = new DateTime(2022, 2, 3), - DateModifiedUtc = new DateTime(2022, 2, 3), - Path = Path.Combine(Documents.Path, "Specification.docx"), - Size = 462 * 1024 - }; - - var report = new FlatFileEntry() - { - Id = "5", - ParentId = Documents.Id, - Name = "Monthly report", - IsDirectory = false, - HasDirectories = false, - Extension = ".xlsx", - DateCreated = new DateTime(2022, 1, 20), - DateCreatedUtc = new DateTime(2022, 1, 20), - DateModified = new DateTime(2022, 1, 25), - DateModifiedUtc = new DateTime(2022, 1, 25), - Path = Path.Combine(Documents.Path, "Monthly report.xlsx"), - Size = 538 * 1024 - }; - - var dashboardDesign = new FlatFileEntry() - { - Id = "6", - ParentId = Images.Id, - Name = "Dashboard Design", - IsDirectory = false, - HasDirectories = false, - Extension = ".png", - DateCreated = new DateTime(2022, 1, 10), - DateCreatedUtc = new DateTime(2022, 1, 10), - DateModified = new DateTime(2022, 2, 13), - DateModifiedUtc = new DateTime(2022, 2, 13), - Path = Path.Combine(Images.Path, "Dashboard Design.png"), - Size = 1024 - }; - - var gridDesign = new FlatFileEntry() - { - Id = "7", - ParentId = Images.Id, - Name = "Grid Design", - IsDirectory = false, - HasDirectories = false, - Extension = ".jpg", - DateCreated = new DateTime(2022, 1, 12), - DateCreatedUtc = new DateTime(2022, 1, 12), - DateModified = new DateTime(2022, 2, 13), - DateModifiedUtc = new DateTime(2022, 2, 13), - Path = Path.Combine(Images.Path, "Grid Design.jpg"), - Size = 1024 - }; - - var files = new List() - { - workFiles, - - Documents, - specification, - report, - - Images, - dashboardDesign, - gridDesign - }; - - return await Task.FromResult(files); + return item; } - public class FlatFileEntry + #endregion FileManager Data Generation + + public class FileManagerItem { - public string Id { get; set; } - public string ParentId { get; set; } - public string Name { get; set; } + public string Id { get; set; } = Guid.NewGuid().ToString(); + public string? ParentId { get; set; } + + public string Name { get; set; } = string.Empty; + public string Extension { get; set; } = string.Empty; public long Size { get; set; } - public string Path { get; set; } - public string Extension { get; set; } + public string Path { get; set; } = string.Empty; + public bool IsDirectory { get; set; } public bool HasDirectories { get; set; } + public DateTime DateCreated { get; set; } public DateTime DateCreatedUtc { get; set; } public DateTime DateModified { get; set; } @@ -282,11 +198,108 @@ The example below demonstrates how to handle successful upload on the FileManage } } ```` +````C# FileManagerController.cs +using Microsoft.AspNetCore.Mvc; + +namespace TelerikBlazorApp.Controllers +{ + [ApiController] + [Route("api/[controller]/[action]")] + public class FileManagerController : ControllerBase + { + public IWebHostEnvironment HostingEnvironment { get; set; } + + public FileManagerController(IWebHostEnvironment hostingEnvironment) + { + HostingEnvironment = hostingEnvironment; + } + + [HttpPost] + public async Task Save(IFormFile files, [FromForm] string fileManagerPath) + { + if (files != null) + { + try + { + if (fileManagerPath.StartsWith("/")) + { + // Path.Combine ignores the first argument if the second one is an absolute path + fileManagerPath = fileManagerPath.Substring(1); + } + + var saveLocation = Path.Combine(new string[] { HostingEnvironment.WebRootPath, fileManagerPath, files.FileName }); + + using (var fileStream = new FileStream(saveLocation, FileMode.Create)) + { + await files.CopyToAsync(fileStream); + } + } + catch (Exception ex) + { + Response.StatusCode = 500; + await Response.WriteAsync($"Upload failed: {ex.Message}"); + } + } + + return new EmptyResult(); + } + + [HttpPost] + public async Task Remove([FromForm] string files, [FromForm] string fileManagerPath) + { + if (files != null) + { + try + { + if (fileManagerPath.StartsWith("/")) + { + fileManagerPath = fileManagerPath.Substring(1); + } + + var fileLocation = Path.Combine(HostingEnvironment.WebRootPath, fileManagerPath, files); + + if (System.IO.File.Exists(fileLocation)) + { + System.IO.File.Delete(fileLocation); + } + } + catch (Exception ex) + { + Response.StatusCode = 500; + await Response.WriteAsync($"Delete failed: {ex.Message}"); + } + } + + return new EmptyResult(); + } + } +} +```` +````C# Program.cs +// ... + +var builder = WebApplication.CreateBuilder(args); + +// ... + +builder.Services.AddControllers(); + +var app = builder.Build(); + +// ... + +app.MapDefaultControllerRoute(); + +// ... + +app.Run(); +```` ## Next Steps * [Upload Events](slug:upload-events) * [Upload Validation](slug:upload-validation) +* [Upload Troubleshooting](slug:upload-troubleshooting) ## See Also diff --git a/components/upload/events.md b/components/upload/events.md index 55e106b07c..f7109a3636 100644 --- a/components/upload/events.md +++ b/components/upload/events.md @@ -490,7 +490,7 @@ public async Task Save(IFormFile files) ## Example -The `UploadController` class below assumes that the project name and namespace is `TelerikBlazorUpload`. +The `UploadController` class below assumes that the project name and namespace is `TelerikBlazorApp`. Make sure to enable controller routing in the app startup file (`Program.cs`). In this case, `app.MapDefaultControllerRoute();` is all that's needed. @@ -688,7 +688,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -namespace TelerikBlazorUpload.Controllers +namespace TelerikBlazorApp.Controllers { [Route("api/[controller]/[action]")] public class UploadController : ControllerBase diff --git a/components/upload/overview.md b/components/upload/overview.md index a831b22eb0..3c4d597afb 100644 --- a/components/upload/overview.md +++ b/components/upload/overview.md @@ -78,7 +78,7 @@ Request routing depends on the application and is outside the Upload component s Also check the [Upload Troubleshooting](slug:upload-troubleshooting) page. -The `UploadController` class below assumes that the project name and namespace is `TelerikBlazorUpload`. +The `UploadController` class below assumes that the project name and namespace is `TelerikBlazorApp`. >caption Sample Upload Controller @@ -92,7 +92,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -namespace TelerikBlazorUpload.Controllers +namespace TelerikBlazorApp.Controllers { [Route("api/[controller]/[action]")] public class UploadController : ControllerBase diff --git a/knowledge-base/upload-preview-image.md b/knowledge-base/upload-preview-image.md index 8615d429d4..f326162686 100644 --- a/knowledge-base/upload-preview-image.md +++ b/knowledge-base/upload-preview-image.md @@ -47,6 +47,8 @@ In Blazor, [previewing images is easier when using the FileSelect](#using-the-fi You cannot preview the image in the Upload `OnSelect` event, because this event handler has no access to the file contents. +The `UploadController` class below assumes that the project name and namespace is `TelerikBlazorApp`. + >caption Preview uploaded images when using the Upload component
@@ -130,7 +132,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -namespace TelerikBlazorUpload.Controllers +namespace TelerikBlazorApp.Controllers { [Route("api/[controller]/[action]")] public class UploadController : ControllerBase