@using Microsoft.Extensions.Options TODO: Handle the case in which there are duplicate file names Drag and drop files here or click @if (_files.Any()) { @foreach (var file in _files) { var color = _fileToDiskFileName.Keys.Contains(file) ? Color.Success : Color.Warning; } } Open file picker Upload Clear @if (_files.Count != _fileToDiskFileName.Count) { *Files must be uploaded } @inject IOptions _options; @inject IFilePathListingProvider PathProvider; @inject ISnackbar Snackbar; @inject ILogger _logger; @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; private readonly List _files = new(); private readonly Dictionary _fileToDiskFileName = new(); private MudFileUpload>? _fileUpload; public int SelectedFileCount { get => _files.Count; } public bool UploadsComplete { get; set; } = true; [Parameter] public EventCallback> FilesUploaded {get; set; } [Parameter] public EventCallback ClearClicked { get; set; } private async Task ClearAsync() { 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); } } _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 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; }