Skip to content

Commit ef8a321

Browse files
committed
chore: add file manager blob storage example
1 parent fc2187a commit ef8a321

File tree

122 files changed

+140266
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

122 files changed

+140266
-0
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.31903.59
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KendoUI.FileManager.BlobStorage", "KendoUI.FileManager.BlobStorage\KendoUI.FileManager.BlobStorage.csproj", "{8C002550-8D5E-43CE-8F1F-A3A7D5149F48}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Debug|x64 = Debug|x64
12+
Debug|x86 = Debug|x86
13+
Release|Any CPU = Release|Any CPU
14+
Release|x64 = Release|x64
15+
Release|x86 = Release|x86
16+
EndGlobalSection
17+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
18+
{8C002550-8D5E-43CE-8F1F-A3A7D5149F48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19+
{8C002550-8D5E-43CE-8F1F-A3A7D5149F48}.Debug|Any CPU.Build.0 = Debug|Any CPU
20+
{8C002550-8D5E-43CE-8F1F-A3A7D5149F48}.Debug|x64.ActiveCfg = Debug|Any CPU
21+
{8C002550-8D5E-43CE-8F1F-A3A7D5149F48}.Debug|x64.Build.0 = Debug|Any CPU
22+
{8C002550-8D5E-43CE-8F1F-A3A7D5149F48}.Debug|x86.ActiveCfg = Debug|Any CPU
23+
{8C002550-8D5E-43CE-8F1F-A3A7D5149F48}.Debug|x86.Build.0 = Debug|Any CPU
24+
{8C002550-8D5E-43CE-8F1F-A3A7D5149F48}.Release|Any CPU.ActiveCfg = Release|Any CPU
25+
{8C002550-8D5E-43CE-8F1F-A3A7D5149F48}.Release|Any CPU.Build.0 = Release|Any CPU
26+
{8C002550-8D5E-43CE-8F1F-A3A7D5149F48}.Release|x64.ActiveCfg = Release|Any CPU
27+
{8C002550-8D5E-43CE-8F1F-A3A7D5149F48}.Release|x64.Build.0 = Release|Any CPU
28+
{8C002550-8D5E-43CE-8F1F-A3A7D5149F48}.Release|x86.ActiveCfg = Release|Any CPU
29+
{8C002550-8D5E-43CE-8F1F-A3A7D5149F48}.Release|x86.Build.0 = Release|Any CPU
30+
EndGlobalSection
31+
GlobalSection(SolutionProperties) = preSolution
32+
HideSolutionNode = FALSE
33+
EndGlobalSection
34+
EndGlobal
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
using KendoUI.FileManager.BlobStorage.Models;
2+
using KendoUI.FileManager.BlobStorage.Helpers;
3+
using Microsoft.AspNetCore.Mvc;
4+
using KendoUI.FileManager.BlobStorage.Services;
5+
using System.Diagnostics;
6+
7+
namespace KendoUI.FileManager.BlobStorage.Controllers
8+
{
9+
public class HomeController : Controller
10+
{
11+
private readonly ILogger<HomeController> _logger;
12+
// Inject the service that handles Azure Blob Storage operations
13+
private readonly IBlobFileManagerService _fileManagerService;
14+
15+
public HomeController(ILogger<HomeController> logger, IBlobFileManagerService fileManagerService)
16+
{
17+
_logger = logger;
18+
_fileManagerService = fileManagerService;
19+
}
20+
21+
public IActionResult Index()
22+
{
23+
return View();
24+
}
25+
26+
public IActionResult Alternative()
27+
{
28+
return View("Index_Alternative");
29+
}
30+
31+
public IActionResult About()
32+
{
33+
ViewData["Message"] = "Your application description page.";
34+
return View();
35+
}
36+
37+
public IActionResult Contact()
38+
{
39+
ViewData["Message"] = "Your contact page.";
40+
return View();
41+
}
42+
43+
public IActionResult Error()
44+
{
45+
return View();
46+
}
47+
48+
// Handles reading files and folders from Azure Blob Storage for the FileManager
49+
[HttpPost]
50+
public async Task<IActionResult> FileManager_Read([FromForm] string target)
51+
{
52+
try
53+
{
54+
// Retrieve the list of blobs/folders from the specified path
55+
var files = await _fileManagerService.ReadAsync(target);
56+
return Json(files);
57+
}
58+
catch (Exception ex)
59+
{
60+
_logger.LogError(ex, "Failed to read FileManager contents.");
61+
return BadRequest(new { error = ex.Message });
62+
}
63+
}
64+
65+
// Handles creating new folders or copying/pasting files in Azure Blob Storage
66+
[HttpPost]
67+
public async Task<IActionResult> FileManager_Create([FromForm] string target, [FromForm] string name, [FromForm] int entry)
68+
{
69+
try
70+
{
71+
// Parse the form data to determine if this is a folder creation, file upload, or copy operation
72+
var context = FileManagerCreateContext.FromForm(Request.Form);
73+
var result = await _fileManagerService.CreateAsync(target, name, entry, context);
74+
return Json(result);
75+
}
76+
catch (Exception ex)
77+
{
78+
_logger.LogError(ex, "Failed to create entry in FileManager.");
79+
return BadRequest(new { error = ex.Message });
80+
}
81+
}
82+
83+
// Handles renaming files or folders in Azure Blob Storage
84+
[HttpPost]
85+
public async Task<IActionResult> FileManager_Update()
86+
{
87+
try
88+
{
89+
var targetPath = Request.Form["path"];
90+
var newName = Request.Form["name"];
91+
92+
if (string.IsNullOrEmpty(targetPath) || string.IsNullOrEmpty(newName))
93+
{
94+
return BadRequest(new { error = "Path and name are required for rename operation" });
95+
}
96+
97+
// Rename is implemented by copying the blob to a new path and deleting the old one
98+
var result = await _fileManagerService.UpdateAsync(targetPath!, newName!);
99+
return Json(result);
100+
}
101+
catch (Exception ex)
102+
{
103+
_logger.LogError(ex, "Failed to rename FileManager entry.");
104+
return BadRequest(new { error = ex.Message });
105+
}
106+
}
107+
108+
// Handles deleting files or folders from Azure Blob Storage
109+
[HttpPost]
110+
public async Task<IActionResult> FileManager_Destroy([FromForm] string models)
111+
{
112+
try
113+
{
114+
// Parse the request to extract the path of the item to delete
115+
var targetPath = FileManagerRequestParser.ResolveTargetPath(Request.Form, models);
116+
if (string.IsNullOrEmpty(targetPath))
117+
{
118+
var formData = string.Join(", ", Request.Form.Select(kvp => $"{kvp.Key}={kvp.Value}"));
119+
return BadRequest(new { error = "No target path provided for deletion. Received: " + formData });
120+
}
121+
122+
await _fileManagerService.DeleteAsync(targetPath);
123+
return Json(Array.Empty<object>());
124+
}
125+
catch (Exception ex)
126+
{
127+
_logger.LogError(ex, "Failed to delete FileManager entry.");
128+
return BadRequest(new { error = ex.Message });
129+
}
130+
}
131+
132+
// Handles file uploads to Azure Blob Storage
133+
[HttpPost]
134+
public async Task<IActionResult> FileManager_Upload([FromForm] string target, IFormFile file)
135+
{
136+
try
137+
{
138+
if (file == null || file.Length == 0)
139+
{
140+
return BadRequest(new { error = "No file uploaded" });
141+
}
142+
143+
// Normalize the target path and upload the file to the blob container
144+
var resolvedTarget = NormalizeUploadTarget(target);
145+
var result = await _fileManagerService.UploadAsync(resolvedTarget, file);
146+
return Json(result);
147+
}
148+
catch (Exception ex)
149+
{
150+
_logger.LogError(ex, "Failed to upload file.");
151+
return BadRequest(new { error = ex.Message });
152+
}
153+
}
154+
155+
private string NormalizeUploadTarget(string? target)
156+
{
157+
var resolvedTarget = target ??
158+
Request.Form["target"].FirstOrDefault() ??
159+
Request.Form["path"].FirstOrDefault() ??
160+
string.Empty;
161+
162+
return string.IsNullOrEmpty(resolvedTarget)
163+
? string.Empty
164+
: resolvedTarget.Trim('/');
165+
}
166+
}
167+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System.Text.Json;
2+
using Microsoft.AspNetCore.Http;
3+
4+
namespace KendoUI.FileManager.BlobStorage.Helpers
5+
{
6+
public static class FileManagerRequestParser
7+
{
8+
public static string? ResolveTargetPath(IFormCollection form, string? models)
9+
{
10+
if (form is null)
11+
{
12+
throw new ArgumentNullException(nameof(form));
13+
}
14+
15+
var targetPath = form["path"].FirstOrDefault() ??
16+
form["target"].FirstOrDefault() ??
17+
form["Name"].FirstOrDefault() ??
18+
form["name"].FirstOrDefault();
19+
20+
if (string.IsNullOrWhiteSpace(models))
21+
{
22+
return targetPath;
23+
}
24+
25+
try
26+
{
27+
var modelsArray = JsonSerializer.Deserialize<JsonElement[]>(models);
28+
if (modelsArray is not { Length: > 0 })
29+
{
30+
return targetPath;
31+
}
32+
33+
var firstModel = modelsArray[0];
34+
if (firstModel.TryGetProperty("path", out var pathElement))
35+
{
36+
targetPath = pathElement.GetString();
37+
}
38+
}
39+
catch
40+
{
41+
// Intentionally swallow JSON parsing errors and fall back to form values
42+
}
43+
44+
return targetPath;
45+
}
46+
}
47+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Azure.Storage.Blobs" Version="12.26.0" />
11+
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.0" />
12+
<PackageReference Include="Telerik.UI.for.AspNet.Core" Version="2025.4.1111" />
13+
</ItemGroup>
14+
15+
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
16+
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
17+
</PropertyGroup>
18+
19+
<ItemGroup>
20+
<Compile Remove="Templates\**" />
21+
<Content Remove="Templates\**" />
22+
<EmbeddedResource Remove="Templates\**" />
23+
<None Remove="Templates\**" />
24+
</ItemGroup>
25+
26+
<ProjectExtensions>
27+
<VisualStudio>
28+
<UserProperties UseCdnSupport="True" />
29+
</VisualStudio>
30+
</ProjectExtensions>
31+
32+
</Project>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Microsoft.AspNetCore.Http;
2+
3+
namespace KendoUI.FileManager.BlobStorage.Models
4+
{
5+
public sealed class FileManagerCreateContext
6+
{
7+
public IFormFile? UploadedFile { get; init; }
8+
public string? SourcePath { get; init; }
9+
public string? Extension { get; init; }
10+
public string? IsDirectoryFlag { get; init; }
11+
12+
public static FileManagerCreateContext FromForm(IFormCollection form)
13+
{
14+
if (form is null)
15+
{
16+
throw new ArgumentNullException(nameof(form));
17+
}
18+
19+
return new FileManagerCreateContext
20+
{
21+
UploadedFile = form.Files.FirstOrDefault(),
22+
SourcePath = form["path"].FirstOrDefault() ??
23+
form["source"].FirstOrDefault() ??
24+
form["sourcePath"].FirstOrDefault(),
25+
Extension = form["extension"].FirstOrDefault(),
26+
IsDirectoryFlag = form["isDirectory"].FirstOrDefault()
27+
};
28+
}
29+
}
30+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace KendoUI.FileManager.BlobStorage.Models
4+
{
5+
public sealed class FileManagerEntry
6+
{
7+
[JsonPropertyName("name")]
8+
public string Name { get; init; } = string.Empty;
9+
10+
[JsonPropertyName("isDirectory")]
11+
public bool IsDirectory { get; init; }
12+
13+
[JsonPropertyName("hasDirectories")]
14+
public bool HasDirectories { get; init; }
15+
16+
[JsonPropertyName("path")]
17+
public string Path { get; init; } = string.Empty;
18+
19+
[JsonPropertyName("extension")]
20+
public string Extension { get; init; } = string.Empty;
21+
22+
[JsonPropertyName("size")]
23+
public long Size { get; init; }
24+
25+
[JsonPropertyName("created")]
26+
public DateTime Created { get; init; }
27+
28+
[JsonPropertyName("createdUtc")]
29+
public DateTime CreatedUtc { get; init; }
30+
31+
[JsonPropertyName("modified")]
32+
public DateTime Modified { get; init; }
33+
34+
[JsonPropertyName("modifiedUtc")]
35+
public DateTime ModifiedUtc { get; init; }
36+
37+
[JsonPropertyName("dateCreated")]
38+
public DateTime DateCreated { get; init; }
39+
40+
[JsonPropertyName("dateModified")]
41+
public DateTime DateModified { get; init; }
42+
}
43+
}

0 commit comments

Comments
 (0)