Working file uploader with chunking

dashboard
Pat Hartl 2023-02-13 00:30:10 -06:00
parent ce0b38d532
commit 0a2f5ee2a5
2 changed files with 38 additions and 40 deletions

View File

@ -1,9 +1,10 @@
@page "/Games/{id:guid}/Archives/Upload"
@using System.Net;
@using System.Diagnostics;
@inject HttpClient HttpClient
@inject NavigationManager Navigator
<MudFileUpload T="IBrowserFile" FilesChanged="FileSelected">
<MudFileUpload T="IBrowserFile" OnFilesChanged="FileSelected">
<ButtonTemplate>
<MudButton HtmlTag="label"
Variant="Variant.Filled"
@ -19,38 +20,32 @@
<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 * 1;
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(IBrowserFile file)
private void FileSelected(InputFileChangeEventArgs args)
{
File = file;
File = args.File;
}
private async Task UploadArchive()
{
var initResponse = await HttpClient.PostAsync("api/Upload/Init", null);
Guid objectKey = Guid.Empty;
if (initResponse.StatusCode == HttpStatusCode.OK)
{
var responseKey = await initResponse.Content.ReadAsStringAsync();
Guid.TryParse(responseKey, out objectKey);
}
var archiveId = Guid.NewGuid();
long uploadedBytes = 0;
long totalBytes = File.Size;
@ -59,6 +54,10 @@
{
Uploading = true;
var watch = new Stopwatch();
watch.Start();
while (Uploading)
{
byte[] chunk;
@ -68,35 +67,31 @@
else
chunk = new byte[ChunkSize];
await stream.ReadAsync(chunk, 0, chunk.Length);
int bytesRead = 0;
using (var formFile = new MultipartFormDataContent())
{
var content = new StreamContent(new MemoryStream(chunk));
formFile.Add(content, "File", File.Name);
formFile.Add(new StringContent(uploadedBytes.ToString()), "Start");
formFile.Add(new StringContent((uploadedBytes + chunk.Length).ToString()), "End");
formFile.Add(new StringContent(objectKey.ToString()), "Key");
formFile.Add(new StringContent(totalBytes.ToString()), "Total");
var response = await HttpClient.PostAsync("api/Upload/Chunk", formFile);
if (response.StatusCode == HttpStatusCode.OK) {
uploadedBytes += chunk.Length;
Progress = (int)(uploadedBytes * 100 / totalBytes);
if (Progress >= 100)
Uploading = false;
}
else
{
Uploading = false;
// Error condition
}
// 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);
}
}

View File

@ -24,6 +24,9 @@ builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor().AddCircuitOptions(option =>
{
option.DetailedErrors = true;
}).AddHubOptions(option =>
{
option.MaximumReceiveMessageSize = 1024 * 1024 * 11;
});
builder.WebHost.ConfigureKestrel(options =>