@page "/storage"
@inject IHttpClientFactory HttpFactory
@inject NavigationManager Nav
@using System.Net.Http.Json
@using Pldpro.Web.Models
Storage
S3 Storage
Buckets
Erstellen
@if (buckets is null)
{
(lädt...)
}
else if (!buckets.Any())
{
(keine Buckets)
}
else
{
@foreach (var b in buckets)
{
@b.Name
}
}
@if (!string.IsNullOrEmpty(selectedBucket))
{
Objekte in '@selectedBucket'
Key
Größe
Geändert
@context.Key
@context.Size
@context.LastModified
Download
@* Name = letzter Segmentteil des Keys *@
@{
var fileName = context.Key.Split('/', StringSplitOptions.RemoveEmptyEntries).LastOrDefault() ?? context.Key;
var encodedName = Uri.EscapeDataString(fileName);
}
Download by Name
Löschen
}
@code {
private record BucketVm(string Name, DateTime? CreationDate);
private record ObjectVm(string Key, long? Size, DateTime? LastModified);
private List? buckets;
private List? objects;
private string? selectedBucket;
private string newBucketName = "";
private string? uploadPath; // optionaler Pfad
private const long StreamLimit = 512L * 1024 * 1024; // 512 MB (Program.cs erhöht Multipart-Limit)
private HttpClient? Http;
protected override Task OnInitializedAsync()
{
Http = HttpFactory.CreateClient();
Http.BaseAddress = new Uri(Nav.BaseUri); // für relative URLs wie "/api/storage/..."
return LoadBuckets();
}
private async Task LoadBuckets()
{
var data = await Http.GetFromJsonAsync>("/api/storage/buckets");
buckets = data ?? new();
StateHasChanged();
}
private async Task SelectBucket(string name)
{
selectedBucket = name;
objects = await Http.GetFromJsonAsync>($"/api/storage/buckets/{name}/objects") ?? new();
}
private async Task CreateBucket()
{
if (string.IsNullOrWhiteSpace(newBucketName)) return;
await Http.PostAsJsonAsync("/api/storage/buckets", new S3CreateBucketDto { BucketName = newBucketName! });
newBucketName = "";
await LoadBuckets();
}
private async Task OnFilesSelected(InputFileChangeEventArgs e)
{
if (string.IsNullOrEmpty(selectedBucket)) return;
foreach (var file in e.GetMultipleFiles())
{
using var stream = file.OpenReadStream(StreamLimit);
using var content = new MultipartFormDataContent();
content.Add(new StreamContent(stream), "file", file.Name);
if (!string.IsNullOrWhiteSpace(uploadPath))
content.Add(new StringContent(uploadPath!), "path");
var resp = await Http.PostAsync($"/api/storage/buckets/{selectedBucket}/upload", content);
resp.EnsureSuccessStatusCode();
}
// Refresh list
objects = await Http.GetFromJsonAsync>($"/api/storage/buckets/{selectedBucket}/objects") ?? new();
}
private static string EncodeKeyForPath(string key)
=> string.Join("/", (key ?? string.Empty)
.Split('/', StringSplitOptions.RemoveEmptyEntries)
.Select(Uri.EscapeDataString));
private string GetDownloadUrl(string key)
{
// JEDES Segment encodieren, Slash erhalten -> /a/b/c
var encodedPath = EncodeKeyForPath(key);
return $"/api/storage/buckets/{selectedBucket}/download/{encodedPath}";
}
private async Task ConfirmAndDelete(string key)
{
// Simple Bestätigung; alternativ MudDialog verwenden
var really = await JSConfirm($"Objekt löschen?\n\nBucket: {selectedBucket}\nKey: {key}");
if (really)
{
await DeleteObject(key);
}
}
private async Task DeleteObject(string key)
{
if (string.IsNullOrEmpty(selectedBucket)) return;
var encodedPath = EncodeKeyForPath(key);
var url = $"/api/storage/buckets/{selectedBucket}/objects/{encodedPath}";
var resp = await Http!.DeleteAsync(url);
if (resp.IsSuccessStatusCode)
{
// Liste aktualisieren
objects = await Http!.GetFromJsonAsync>($"/api/storage/buckets/{selectedBucket}/objects") ?? new();
StateHasChanged();
}
else
{
var msg = await resp.Content.ReadAsStringAsync();
throw new InvalidOperationException($"Delete fehlgeschlagen: {(int)resp.StatusCode} {resp.ReasonPhrase}\n{msg}");
}
}
// Sehr einfache JS-Confirm-Hilfe (füge IJSRuntime-Injection hinzu)
[Inject] private IJSRuntime JS { get; set; } = default!;
private async Task JSConfirm(string message)
=> await JS.InvokeAsync("confirm", message);
}