Files
openarchival/OpenArchival.Blazor.CustomComponents/UploadDropBox.razor

270 lines
8.8 KiB
Plaintext

@namespace OpenArchival.Blazor.CustomComponents
@using Microsoft.Extensions.Logging
@using Microsoft.Extensions.Options
@using OpenArchival.DataAccess
@using Microsoft.AspNetCore.Components.Forms
@using MudBlazor
@using OpenArchival.Blazor.Config
<style>
.file-upload-input {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 10;
opacity: 0;
}
</style>
<MudStack Style="width: 100%">
<MudFileUpload T="IReadOnlyList<IBrowserFile>"
@ref="@_fileUpload"
OnFilesChanged="OnInputFileChanged"
AppendMultipleFiles=true
MaximumFileCount="_options.Value.MaxFileCount"
Hidden="@false"
InputClass="file-upload-input"
tabindex="-1"
@ondrop="@ClearDragClass"
@ondragenter="@SetDragClass"
@ondragleave="@ClearDragClass"
@ondragend="@ClearDragClass">
<ActivatorContent>
<MudPaper Outlined="true"
Class="@_dragClass"
Style="height: 150px; display: flex; flex-direction: column; position: relative;">
<div class="d-flex align-center justify-center pa-4" style="flex-shrink: 0;">
<MudText Typo="Typo.h6">
Drag and drop files here or click
</MudText>
</div>
</MudPaper>
</ActivatorContent>
</MudFileUpload>
@if (Files.Any() || ExistingFiles.Any())
{
<MudPaper Style="max-height: 150px; overflow-y: auto;" Outlined="true" Class="pa-4">
@foreach (var file in Files)
{
var color = _fileToDiskFileName.Keys.Contains(file) ? Color.Success : Color.Warning;
<MudChip T="string"
Color="@color"
Text="@file.Name"
tabindex="-1" />
}
@foreach (var filelisting in ExistingFiles)
{
<MudChip T="string"
Color="@Color.Success"
Text="@filelisting.OriginalName"
tabindex="-1" />
}
</MudPaper>
}
<MudToolBar Gutters="@false"
Class="relative d-flex justify-end gap-4">
<MudButton Color="Color.Primary"
OnClick="@OpenFilePickerAsync"
Variant="Variant.Filled">
Open file picker
</MudButton>
<MudButton Color="Color.Primary"
Disabled="@(!Files.Any())"
OnClick="@Upload"
Variant="Variant.Filled">
Upload
</MudButton>
<MudButton Color="Color.Error"
Disabled="@(!Files.Any())"
OnClick="@ClearAsync"
Variant="Variant.Filled">
Clear
</MudButton>
</MudToolBar>
@if (Files.Count != _fileToDiskFileName.Count)
{
<MudText Color="Color.Error" Align="Align.Right">*Files must be uploaded</MudText>
}
</MudStack>
@inject IOptions<FileUploadOptions> _options;
@inject IFilePathListingProvider PathProvider;
@inject ISnackbar Snackbar;
@inject ILogger<UploadDropBox> _logger;
@inject IDialogService DialogService;
@code {
private const string DefaultDragClass = "relative rounded-lg border-2 border-dashed pa-4 mt-4 mud-width-full mud-height-full";
private string _dragClass = DefaultDragClass;
public readonly List<IBrowserFile> Files = new();
private readonly Dictionary<IBrowserFile, string> _fileToDiskFileName = new();
private MudFileUpload<IReadOnlyList<IBrowserFile>>? _fileUpload;
public int SelectedFileCount { get => Files.Count; }
public bool UploadsComplete { get; set; } = true;
[Parameter]
public EventCallback<List<FilePathListing>> FilesUploaded {get; set; }
[Parameter]
public EventCallback ClearClicked { get; set; }
[Parameter]
public List<FilePathListing> ExistingFiles { get; set; } = new();
protected override Task OnParametersSetAsync()
{
StateHasChanged();
return base.OnParametersSetAsync();
}
private async Task ClearAsync()
{
bool? confirmed = await DialogService.ShowMessageBox
(
new MessageBoxOptions(){
Message=$"Are you sure you want to clear files? You will loose all changes.",
Title="Clear Files",
CancelText="Cancel",
YesText="Clear"
});
if (confirmed is not null && (confirmed ?? throw new ArgumentNullException("confirmed was null")))
{
foreach (var pair in _fileToDiskFileName)
{
try
{
FileInfo targetFile = new(pair.Value);
if (targetFile.Exists)
{
targetFile.Delete();
}
await PathProvider.DeleteFilePathListingAsync(pair.Key.Name, pair.Value);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error deleting file {FileName}", pair.Key.Name);
Snackbar.Add($"Error cleaning up file: {pair.Key.Name}", Severity.Warning);
}
}
foreach (var listing in ExistingFiles)
{
try
{
FileInfo targetFile = new(listing.Path);
if (targetFile.Exists)
{
targetFile.Delete();
}
await PathProvider.DeleteFilePathListingAsync(listing.OriginalName, listing.Path);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error deleting file {listing.Path}");
Snackbar.Add($"Error cleaning up file: {listing.OriginalName}", Severity.Warning);
}
}
_fileToDiskFileName.Clear();
Files.Clear();
await (_fileUpload?.ClearAsync() ?? Task.CompletedTask);
ClearDragClass();
UploadsComplete = true;
await ClearClicked.InvokeAsync();
}
}
private Task OpenFilePickerAsync()
=> _fileUpload?.OpenFilePickerAsync() ?? Task.CompletedTask;
private void OnInputFileChanged(InputFileChangeEventArgs e)
{
ClearDragClass();
var files = e.GetMultipleFiles(maximumFileCount: _options.Value.MaxFileCount);
Files.AddRange(files);
UploadsComplete = false;
StateHasChanged();
}
private async Task Upload()
{
if (!Files.Any())
{
Snackbar.Add("No files to upload.", Severity.Warning);
return;
}
Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;
List<FilePathListing> fileListings = [];
foreach (var file in Files)
{
if (_fileToDiskFileName.ContainsKey(file)) continue;
try
{
var diskFileName = $"{Guid.NewGuid()}{Path.GetExtension(file.Name)}";
var destinationPath = Path.Combine(_options.Value.UploadFolderPath, diskFileName);
await using var browserUploadStream = file.OpenReadStream(maxAllowedSize: _options.Value.MaxUploadSizeBytes);
await using var outFileStream = new FileStream(destinationPath, FileMode.Create);
await browserUploadStream.CopyToAsync(outFileStream);
_fileToDiskFileName.Add(file, destinationPath);
var fileListing = new FilePathListing() { Path = destinationPath, OriginalName = Path.GetFileName(file.Name) };
fileListings.Add(fileListing);
await PathProvider.CreateFilePathListingAsync(fileListing);
Snackbar.Add($"Uploaded {file.Name}", Severity.Success);
UploadsComplete = true;
}
catch (Exception ex)
{
Snackbar.Add($"Error uploading file {file.Name}: {ex.Message}", Severity.Error);
}
}
await FilesUploaded.InvokeAsync(fileListings);
}
private void SetDragClass()
=> _dragClass = $"{DefaultDragClass} mud-border-primary";
private void ClearDragClass()
=> _dragClass = DefaultDragClass;
public void RemoveFile(string filename)
{
var existingFile = ExistingFiles.Where(f => f.OriginalName == filename).FirstOrDefault();
if (existingFile is not null)
{
ExistingFiles.Remove(existingFile);
}
var file = Files.Where(f => f.Name == filename).FirstOrDefault();
if (file is not null)
{
Files.Remove(file);
}
}
}