298 lines
10 KiB
Plaintext
298 lines
10 KiB
Plaintext
@page "/manage-uploads"
|
|
@attribute [Authorize]
|
|
@using BLAIzor.Components.Layout
|
|
@using BLAIzor.Models.Editor
|
|
@using BLAIzor.Services
|
|
@using Microsoft.AspNetCore.Components.Authorization
|
|
@using SixLabors.ImageSharp
|
|
@using SixLabors.ImageSharp.Processing
|
|
@layout AdminLayout
|
|
@inject NavigationManager NavigationManager
|
|
@inject IHttpContextAccessor HttpContextAccessor
|
|
@inject AuthenticationStateProvider AuthenticationStateProvider
|
|
@inject CustomAuthenticationStateProvider CustomAuthProvider
|
|
@inject IJSRuntime JSRuntime
|
|
|
|
@if (IsLoading)
|
|
{
|
|
<p>Loading content...</p>
|
|
}
|
|
else
|
|
{
|
|
<h3>Manage Uploaded Files</h3>
|
|
@if (files == null || (!files.Images.Any() && !files.Videos.Any() && !files.Audio.Any()))
|
|
{
|
|
<p>No files uploaded yet.</p>
|
|
}
|
|
else
|
|
{
|
|
<div>
|
|
<h4>Uploaded Images</h4>
|
|
|
|
|
|
<RadzenRow class="rz-text-align-center" Gap="1rem">
|
|
|
|
|
|
@foreach (var image in files.Images)
|
|
{
|
|
|
|
<RadzenColumn Size="6" SizeXS="12" SizeSM="6" SizeMD="4" SizeLG="3" SizeXL="2" SizeXX="2" class="rz-color-on-info-lighter rz-p-5">
|
|
<div class="card">
|
|
<div class="upload-image-container" @onclick="() => CopyToClipboard(image.OriginalUrl)">
|
|
<img class="img-fluid square-thumbnail" src="@image.ThumbnailUrl" alt="Uploaded Image" loading="lazy" />
|
|
</div>
|
|
<button class="btn btn-danger" @onclick="@(() => DeleteFile(image.OriginalUrl, "Images"))">Delete</button>
|
|
</div>
|
|
</RadzenColumn>
|
|
|
|
}
|
|
</RadzenRow>
|
|
</div>
|
|
<div>
|
|
<h4>Uploaded Videos</h4>
|
|
<div class="row">
|
|
@foreach (var video in files.Videos)
|
|
{
|
|
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
|
|
<div class="card">
|
|
<video controls style="width: 100%; height: auto">
|
|
<source src="@video" type="video/mp4">
|
|
</video>
|
|
<button class="btn btn-danger" @onclick="@(() => DeleteFile(video, "Videos"))">Delete</button>
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h4>Uploaded Audio</h4>
|
|
<div class="row">
|
|
@foreach (var audio in files.Audio)
|
|
{
|
|
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
|
|
<div class="card">
|
|
<audio controls>
|
|
<source src="@audio" type="audio/mpeg">
|
|
</audio>
|
|
<button class="btn btn-danger" @onclick="@(() => DeleteFile(audio, "Audio"))">Delete</button>
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
<InputFile class="btn btn-default" type="file" multiple OnChange=HandleFileUpload accept=".mp3,.mp4,.jpg,.png" />
|
|
}
|
|
}
|
|
|
|
|
|
@code {
|
|
private UploadedFilesModel files = new UploadedFilesModel(); // Properly declared and initialized
|
|
private string? userId;
|
|
private string? userName;
|
|
private AuthenticationState? authState;
|
|
public bool IsLoading = false;
|
|
|
|
private async Task CopyToClipboard(string filePath)
|
|
{
|
|
await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", filePath);
|
|
await JSRuntime.InvokeVoidAsync("alert", "FilePath copied to clipboard");
|
|
}
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
|
if (authState.User.Identity?.IsAuthenticated == true)
|
|
{
|
|
userId = CustomAuthProvider.GetUserId();
|
|
userName = CustomAuthProvider.GetUserName();
|
|
}
|
|
|
|
await LoadFiles();
|
|
}
|
|
|
|
private async Task LoadFiles()
|
|
{
|
|
var basePath = Path.Combine("wwwroot", "uploads", userId);
|
|
|
|
// Load IMAGES
|
|
var imagesPath = Path.Combine(basePath, "images");
|
|
var thumbnailsPath = Path.Combine(imagesPath, "thumbnails");
|
|
|
|
if (!Directory.Exists(thumbnailsPath))
|
|
{
|
|
Directory.CreateDirectory(thumbnailsPath);
|
|
}
|
|
|
|
if (Directory.Exists(imagesPath))
|
|
{
|
|
var imageFiles = Directory.GetFiles(imagesPath)
|
|
.Where(f => !Path.GetFileName(f).Equals("thumbnails", StringComparison.OrdinalIgnoreCase)) // skip folder
|
|
.Where(f => !Directory.Exists(f)) // skip directories
|
|
.Select(f =>
|
|
{
|
|
var fileName = Path.GetFileName(f);
|
|
var originalUrl = $"/uploads/{userId}/images/{fileName}";
|
|
var thumbnailPath = Path.Combine(thumbnailsPath, fileName);
|
|
var thumbnailUrl = $"/uploads/{userId}/images/thumbnails/{fileName}";
|
|
|
|
// Generate thumbnail if missing
|
|
if (!File.Exists(thumbnailPath))
|
|
{
|
|
try
|
|
{
|
|
using var image = Image.Load(f);
|
|
image.Mutate(x => x.Resize(new ResizeOptions
|
|
{
|
|
Mode = ResizeMode.Max,
|
|
Size = new Size(300, 300)
|
|
}));
|
|
image.Save(thumbnailPath); // Will auto-detect format
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"❌ Error generating thumbnail for {fileName}: {ex.Message}");
|
|
thumbnailUrl = string.Empty;
|
|
}
|
|
}
|
|
|
|
return new UploadedImage
|
|
{
|
|
OriginalUrl = originalUrl,
|
|
ThumbnailUrl = thumbnailUrl
|
|
};
|
|
})
|
|
.ToList();
|
|
|
|
files.Images = imageFiles;
|
|
}
|
|
else
|
|
{
|
|
files.Images = new List<UploadedImage>();
|
|
}
|
|
|
|
// Load VIDEOS
|
|
var videosPath = Path.Combine(basePath, "videos");
|
|
files.Videos = Directory.Exists(videosPath)
|
|
? Directory.GetFiles(videosPath)
|
|
.Select(f => $"/uploads/{userId}/videos/{Path.GetFileName(f)}")
|
|
.ToList()
|
|
: new List<string>();
|
|
|
|
// Load AUDIO
|
|
var audioPath = Path.Combine(basePath, "audio");
|
|
files.Audio = Directory.Exists(audioPath)
|
|
? Directory.GetFiles(audioPath)
|
|
.Select(f => $"/uploads/{userId}/audio/{Path.GetFileName(f)}")
|
|
.ToList()
|
|
: new List<string>();
|
|
}
|
|
|
|
|
|
|
|
private async Task DeleteFile(string filePath, string fileType)
|
|
{
|
|
|
|
var physicalPath = Path.Combine("wwwroot", "uploads", userId, fileType.ToLower(), Path.GetFileName(filePath));
|
|
|
|
if (File.Exists(physicalPath))
|
|
{
|
|
File.Delete(physicalPath);
|
|
await LoadFiles();
|
|
}
|
|
}
|
|
|
|
private async Task HandleFileUpload(InputFileChangeEventArgs e)
|
|
{
|
|
if (e.FileCount == 0) return;
|
|
|
|
IsLoading = true;
|
|
|
|
try
|
|
{
|
|
var uploadPath = Path.Combine("wwwroot", "uploads", userId);
|
|
|
|
foreach (var file in e.GetMultipleFiles())
|
|
{
|
|
var folder = GetFolderForFile(file.ContentType);
|
|
var folderPath = Path.Combine(uploadPath, folder);
|
|
|
|
// Create target directory
|
|
Directory.CreateDirectory(folderPath);
|
|
|
|
var filePath = Path.Combine(folderPath, file.Name);
|
|
await using (var stream = new FileStream(filePath, FileMode.Create))
|
|
{
|
|
await file.OpenReadStream(50 * 1024 * 1024).CopyToAsync(stream);
|
|
}
|
|
|
|
var relativePath = $"/uploads/{userId}/{folder}/{file.Name}";
|
|
AppendFilePathToContent(file.ContentType, relativePath);
|
|
|
|
// Generate thumbnail if it's an image
|
|
string? thumbnailRelativePath = null;
|
|
|
|
if (file.ContentType.StartsWith("image/"))
|
|
{
|
|
var thumbnailFolder = Path.Combine(folderPath, "thumbnails");
|
|
Directory.CreateDirectory(thumbnailFolder);
|
|
|
|
var thumbnailPath = Path.Combine(thumbnailFolder, file.Name);
|
|
using var image = await Image.LoadAsync(file.OpenReadStream());
|
|
image.Mutate(x => x.Resize(new ResizeOptions
|
|
{
|
|
Size = new Size(300, 0),
|
|
Mode = ResizeMode.Max
|
|
}));
|
|
await image.SaveAsync(thumbnailPath);
|
|
|
|
thumbnailRelativePath = $"/uploads/{userId}/{folder}/thumbnails/{file.Name}";
|
|
}
|
|
|
|
AppendFilePathToContent(file.ContentType, relativePath, thumbnailRelativePath);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Error uploading files: {ex.Message}");
|
|
}
|
|
finally
|
|
{
|
|
IsLoading = false;
|
|
}
|
|
}
|
|
|
|
private string GetFolderForFile(string contentType)
|
|
{
|
|
return contentType switch
|
|
{
|
|
var type when type.StartsWith("image/") => "images",
|
|
var type when type.StartsWith("video/") => "videos",
|
|
var type when type.StartsWith("audio/") => "audio",
|
|
_ => "others"
|
|
};
|
|
}
|
|
|
|
private void AppendFilePathToContent(string contentType, string relativePath, string? thumbnailPath = null)
|
|
{
|
|
if (contentType.StartsWith("image/"))
|
|
{
|
|
files.Images.Add(new UploadedImage
|
|
{
|
|
OriginalUrl = relativePath,
|
|
ThumbnailUrl = thumbnailPath ?? string.Empty
|
|
});
|
|
}
|
|
else if (contentType.StartsWith("video/"))
|
|
{
|
|
files.Videos.Add(relativePath);
|
|
}
|
|
else if (contentType.StartsWith("audio/"))
|
|
{
|
|
files.Audio.Add(relativePath);
|
|
}
|
|
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|