Moved archive uploader to a dialog component. Reload list of archives on upload complete.
parent
0f9fe9a0ca
commit
e5c01381a0
|
@ -0,0 +1,172 @@
|
||||||
|
@using System.Net;
|
||||||
|
@using System.Diagnostics;
|
||||||
|
@inject HttpClient HttpClient
|
||||||
|
@inject NavigationManager Navigator
|
||||||
|
@inject ISnackbar Snackbar
|
||||||
|
@inject ArchiveService ArchiveService
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<DialogContent>
|
||||||
|
<MudForm @bind-IsValid="@IsValid">
|
||||||
|
<MudTextField T="string" @bind-Value="Archive.Version" Label="Version" Required="true" Disabled="Uploading" RequiredError="Version is required" />
|
||||||
|
<MudTextField T="string" @bind-Value="Archive.Changelog" Label="Changelog" Required="false" Disabled="Uploading" Lines="6" />
|
||||||
|
|
||||||
|
<MudFileUpload T="IBrowserFile" OnFilesChanged="FileSelected" Class="flex-1"
|
||||||
|
InputClass="absolute mud-width-full mud-height-full overflow-hidden z-20 d-block" InputStyle="opacity: 0;"
|
||||||
|
@ondragenter="@SetDragClass" @ondragleave="@ClearDragClass" @ondragend="@ClearDragClass">
|
||||||
|
<ButtonTemplate>
|
||||||
|
<MudPaper Height="200px" Outlined="true" Class="@DragClass">
|
||||||
|
<MudText Typo="Typo.h6">Drop files here or click to browse</MudText>
|
||||||
|
|
||||||
|
@if (File != null)
|
||||||
|
{
|
||||||
|
<MudChip Color="Color.Dark" Text="@File.Name" />
|
||||||
|
}
|
||||||
|
</MudPaper>
|
||||||
|
</ButtonTemplate>
|
||||||
|
</MudFileUpload>
|
||||||
|
|
||||||
|
<MudProgressLinear Color="Color.Primary" Striped="Uploading" Size="Size.Large" Value="Progress" Class="mt-4" />
|
||||||
|
|
||||||
|
<MudText>@ByteSizeLib.ByteSize.FromBytes(Speed)/s</MudText>
|
||||||
|
|
||||||
|
<MudToolBar DisableGutters="true" Class="gap-4">
|
||||||
|
<MudButton OnClick="UploadArchive" Disabled="@(!IsValid || File == null || Uploading)" Color="Color.Primary" Variant="Variant.Filled">Upload</MudButton>
|
||||||
|
<MudButton OnClick="Clear" Disabled="File == null || Uploading" Color="Color.Error" Variant="Variant.Filled">Clear</MudButton>
|
||||||
|
<MudButton OnClick="Cancel">Cancel</MudButton>
|
||||||
|
</MudToolBar>
|
||||||
|
</MudForm>
|
||||||
|
</DialogContent>
|
||||||
|
</MudDialog>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
|
||||||
|
[Parameter] public Guid GameId { get; set; }
|
||||||
|
|
||||||
|
Archive Archive;
|
||||||
|
|
||||||
|
IBrowserFile File { get; set; }
|
||||||
|
|
||||||
|
bool IsValid = false;
|
||||||
|
|
||||||
|
private static string DefaultDragClass = "relative rounded-lg border-2 border-dashed pa-4 mt-4 mud-width-full mud-height-full z-10";
|
||||||
|
private string DragClass = DefaultDragClass;
|
||||||
|
|
||||||
|
const int ChunkSize = 1024 * 1024 * 10;
|
||||||
|
|
||||||
|
int Progress = 0;
|
||||||
|
bool Uploading = false;
|
||||||
|
double Speed = 0;
|
||||||
|
|
||||||
|
Stopwatch Watch;
|
||||||
|
long WatchBytesTransferred = 0;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
HttpClient.BaseAddress = new Uri(Navigator.BaseUri);
|
||||||
|
|
||||||
|
Archive = new Archive()
|
||||||
|
{
|
||||||
|
GameId = GameId,
|
||||||
|
Id = Guid.NewGuid()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetDragClass()
|
||||||
|
{
|
||||||
|
DragClass = $"{DefaultDragClass} mud-border-primary";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearDragClass()
|
||||||
|
{
|
||||||
|
DragClass = DefaultDragClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Clear()
|
||||||
|
{
|
||||||
|
File = null;
|
||||||
|
ClearDragClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel()
|
||||||
|
{
|
||||||
|
MudDialog.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FileSelected(InputFileChangeEventArgs args)
|
||||||
|
{
|
||||||
|
File = args.File;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UploadArchive()
|
||||||
|
{
|
||||||
|
long uploadedBytes = 0;
|
||||||
|
long totalBytes = File.Size;
|
||||||
|
|
||||||
|
Watch = new Stopwatch();
|
||||||
|
|
||||||
|
using (var stream = File.OpenReadStream(long.MaxValue))
|
||||||
|
{
|
||||||
|
Uploading = true;
|
||||||
|
|
||||||
|
Watch.Start();
|
||||||
|
|
||||||
|
while (Uploading)
|
||||||
|
{
|
||||||
|
byte[] chunk;
|
||||||
|
|
||||||
|
if (totalBytes - uploadedBytes < ChunkSize)
|
||||||
|
chunk = new byte[totalBytes - uploadedBytes];
|
||||||
|
else
|
||||||
|
chunk = new byte[ChunkSize];
|
||||||
|
|
||||||
|
int bytesRead = 0;
|
||||||
|
|
||||||
|
// This feels hacky, why do we need to do this?
|
||||||
|
// Only 32256 bytes of the file get read unless we
|
||||||
|
// loop through like this. Probably kills performance.
|
||||||
|
while (bytesRead < chunk.Length)
|
||||||
|
{
|
||||||
|
bytesRead += await stream.ReadAsync(chunk, bytesRead, chunk.Length - bytesRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (FileStream fs = new FileStream(Path.Combine("Upload", Archive.Id.ToString()), FileMode.Append))
|
||||||
|
{
|
||||||
|
await fs.WriteAsync(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadedBytes += chunk.Length;
|
||||||
|
WatchBytesTransferred += chunk.Length;
|
||||||
|
|
||||||
|
Progress = (int)(uploadedBytes * 100 / totalBytes);
|
||||||
|
|
||||||
|
if (Watch.Elapsed.TotalSeconds >= 1)
|
||||||
|
{
|
||||||
|
Speed = WatchBytesTransferred * (1 / Watch.Elapsed.TotalSeconds);
|
||||||
|
WatchBytesTransferred = 0;
|
||||||
|
Watch.Restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Progress >= 100)
|
||||||
|
{
|
||||||
|
Watch.Stop();
|
||||||
|
Uploading = false;
|
||||||
|
UploadComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UploadComplete()
|
||||||
|
{
|
||||||
|
Archive.ObjectKey = Archive.Id.ToString();
|
||||||
|
Archive.CompressedSize = File.Size;
|
||||||
|
|
||||||
|
ArchiveService.Add(Archive);
|
||||||
|
|
||||||
|
MudDialog.Close();
|
||||||
|
Snackbar.Add("Archive uploaded!", Severity.Success);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,99 +0,0 @@
|
||||||
@page "/Games/{id:guid}/Archives/Upload"
|
|
||||||
@using System.Net;
|
|
||||||
@using System.Diagnostics;
|
|
||||||
@inject HttpClient HttpClient
|
|
||||||
@inject NavigationManager Navigator
|
|
||||||
|
|
||||||
<MudFileUpload T="IBrowserFile" OnFilesChanged="FileSelected">
|
|
||||||
<ButtonTemplate>
|
|
||||||
<MudButton HtmlTag="label"
|
|
||||||
Variant="Variant.Filled"
|
|
||||||
Color="Color.Primary"
|
|
||||||
StartIcon="@Icons.Material.Filled.CloudUpload"
|
|
||||||
for="@context">
|
|
||||||
Upload Archive
|
|
||||||
</MudButton>
|
|
||||||
</ButtonTemplate>
|
|
||||||
</MudFileUpload>
|
|
||||||
|
|
||||||
<MudButton OnClick="UploadArchive">Upload</MudButton>
|
|
||||||
|
|
||||||
<MudProgressLinear Color="Color.Primary" Striped="Uploading" Size="Size.Large" Value="Progress" />
|
|
||||||
|
|
||||||
<MudText>@ByteSizeLib.ByteSize.FromBytes(Speed)/s</MudText>
|
|
||||||
|
|
||||||
@code {
|
|
||||||
[Parameter] public Guid Id { get; set; }
|
|
||||||
|
|
||||||
IBrowserFile File { get; set; }
|
|
||||||
|
|
||||||
const int ChunkSize = 1024 * 1024 * 10;
|
|
||||||
|
|
||||||
int Progress = 0;
|
|
||||||
bool Uploading = false;
|
|
||||||
double Speed = 0;
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
HttpClient.BaseAddress = new Uri(Navigator.BaseUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FileSelected(InputFileChangeEventArgs args)
|
|
||||||
{
|
|
||||||
File = args.File;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task UploadArchive()
|
|
||||||
{
|
|
||||||
var archiveId = Guid.NewGuid();
|
|
||||||
|
|
||||||
long uploadedBytes = 0;
|
|
||||||
long totalBytes = File.Size;
|
|
||||||
|
|
||||||
using (var stream = File.OpenReadStream(long.MaxValue))
|
|
||||||
{
|
|
||||||
Uploading = true;
|
|
||||||
|
|
||||||
var watch = new Stopwatch();
|
|
||||||
|
|
||||||
watch.Start();
|
|
||||||
|
|
||||||
while (Uploading)
|
|
||||||
{
|
|
||||||
byte[] chunk;
|
|
||||||
|
|
||||||
if (totalBytes - uploadedBytes < ChunkSize)
|
|
||||||
chunk = new byte[totalBytes - uploadedBytes];
|
|
||||||
else
|
|
||||||
chunk = new byte[ChunkSize];
|
|
||||||
|
|
||||||
int bytesRead = 0;
|
|
||||||
|
|
||||||
// This feels hacky, why do we need to do this?
|
|
||||||
// Only 32256 bytes of the file get read unless we
|
|
||||||
// loop through like this. Probably kills performance.
|
|
||||||
while (bytesRead < chunk.Length) {
|
|
||||||
bytesRead += await stream.ReadAsync(chunk, bytesRead, chunk.Length - bytesRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
using (FileStream fs = new FileStream(Path.Combine("Upload", archiveId.ToString()), FileMode.Append))
|
|
||||||
{
|
|
||||||
await fs.WriteAsync(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadedBytes += chunk.Length;
|
|
||||||
|
|
||||||
Progress = (int)(uploadedBytes * 100 / totalBytes);
|
|
||||||
|
|
||||||
if (Progress >= 100)
|
|
||||||
Uploading = false;
|
|
||||||
|
|
||||||
Speed = chunk.Length * (1 / watch.Elapsed.TotalSeconds);
|
|
||||||
|
|
||||||
watch.Restart();
|
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -125,7 +125,7 @@
|
||||||
</CardHeaderContent>
|
</CardHeaderContent>
|
||||||
|
|
||||||
<CardHeaderActions>
|
<CardHeaderActions>
|
||||||
<MudButton Href="@($"/Games/{Id}/Archives/Upload")" Color="Color.Primary" Variant="Variant.Filled">Upload</MudButton>
|
<MudButton OnClick="() => UploadArchive()" Color="Color.Primary" Variant="Variant.Filled">Upload</MudButton>
|
||||||
</CardHeaderActions>
|
</CardHeaderActions>
|
||||||
</MudCardHeader>
|
</MudCardHeader>
|
||||||
|
|
||||||
|
@ -247,6 +247,21 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void UploadArchive()
|
||||||
|
{
|
||||||
|
var parameters = new DialogParameters
|
||||||
|
{
|
||||||
|
["GameId"] = Game.Id
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialog = await DialogService.ShowAsync<ArchiveUploader>("Archive Uploader", parameters);
|
||||||
|
var result = await dialog.Result;
|
||||||
|
|
||||||
|
await GameService.Context.Entry(Game).Collection(nameof(Game.Archives)).LoadAsync();
|
||||||
|
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
private async void BrowseArchive(Archive archive)
|
private async void BrowseArchive(Archive archive)
|
||||||
{
|
{
|
||||||
var parameters = new DialogParameters
|
var parameters = new DialogParameters
|
||||||
|
|
Loading…
Reference in New Issue