Working file uploader with chunking
This commit is contained in:
parent
ce0b38d532
commit
0a2f5ee2a5
2 changed files with 38 additions and 40 deletions
|
@ -1,9 +1,10 @@
|
||||||
@page "/Games/{id:guid}/Archives/Upload"
|
@page "/Games/{id:guid}/Archives/Upload"
|
||||||
@using System.Net;
|
@using System.Net;
|
||||||
|
@using System.Diagnostics;
|
||||||
@inject HttpClient HttpClient
|
@inject HttpClient HttpClient
|
||||||
@inject NavigationManager Navigator
|
@inject NavigationManager Navigator
|
||||||
|
|
||||||
<MudFileUpload T="IBrowserFile" FilesChanged="FileSelected">
|
<MudFileUpload T="IBrowserFile" OnFilesChanged="FileSelected">
|
||||||
<ButtonTemplate>
|
<ButtonTemplate>
|
||||||
<MudButton HtmlTag="label"
|
<MudButton HtmlTag="label"
|
||||||
Variant="Variant.Filled"
|
Variant="Variant.Filled"
|
||||||
|
@ -19,38 +20,32 @@
|
||||||
|
|
||||||
<MudProgressLinear Color="Color.Primary" Striped="Uploading" Size="Size.Large" Value="Progress" />
|
<MudProgressLinear Color="Color.Primary" Striped="Uploading" Size="Size.Large" Value="Progress" />
|
||||||
|
|
||||||
|
<MudText>@ByteSizeLib.ByteSize.FromBytes(Speed)/s</MudText>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] public Guid Id { get; set; }
|
[Parameter] public Guid Id { get; set; }
|
||||||
|
|
||||||
IBrowserFile File { get; set; }
|
IBrowserFile File { get; set; }
|
||||||
|
|
||||||
const int ChunkSize = 1024 * 1024 * 1;
|
const int ChunkSize = 1024 * 1024 * 10;
|
||||||
|
|
||||||
int Progress = 0;
|
int Progress = 0;
|
||||||
bool Uploading = false;
|
bool Uploading = false;
|
||||||
|
double Speed = 0;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
HttpClient.BaseAddress = new Uri(Navigator.BaseUri);
|
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()
|
private async Task UploadArchive()
|
||||||
{
|
{
|
||||||
var initResponse = await HttpClient.PostAsync("api/Upload/Init", null);
|
var archiveId = Guid.NewGuid();
|
||||||
|
|
||||||
Guid objectKey = Guid.Empty;
|
|
||||||
|
|
||||||
if (initResponse.StatusCode == HttpStatusCode.OK)
|
|
||||||
{
|
|
||||||
var responseKey = await initResponse.Content.ReadAsStringAsync();
|
|
||||||
|
|
||||||
Guid.TryParse(responseKey, out objectKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
long uploadedBytes = 0;
|
long uploadedBytes = 0;
|
||||||
long totalBytes = File.Size;
|
long totalBytes = File.Size;
|
||||||
|
@ -59,6 +54,10 @@
|
||||||
{
|
{
|
||||||
Uploading = true;
|
Uploading = true;
|
||||||
|
|
||||||
|
var watch = new Stopwatch();
|
||||||
|
|
||||||
|
watch.Start();
|
||||||
|
|
||||||
while (Uploading)
|
while (Uploading)
|
||||||
{
|
{
|
||||||
byte[] chunk;
|
byte[] chunk;
|
||||||
|
@ -68,35 +67,31 @@
|
||||||
else
|
else
|
||||||
chunk = new byte[ChunkSize];
|
chunk = new byte[ChunkSize];
|
||||||
|
|
||||||
await stream.ReadAsync(chunk, 0, chunk.Length);
|
int bytesRead = 0;
|
||||||
|
|
||||||
using (var formFile = new MultipartFormDataContent())
|
// This feels hacky, why do we need to do this?
|
||||||
{
|
// Only 32256 bytes of the file get read unless we
|
||||||
var content = new StreamContent(new MemoryStream(chunk));
|
// loop through like this. Probably kills performance.
|
||||||
|
while (bytesRead < chunk.Length) {
|
||||||
formFile.Add(content, "File", File.Name);
|
bytesRead += await stream.ReadAsync(chunk, bytesRead, chunk.Length - bytesRead);
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
await InvokeAsync(StateHasChanged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,9 @@ builder.Services.AddRazorPages();
|
||||||
builder.Services.AddServerSideBlazor().AddCircuitOptions(option =>
|
builder.Services.AddServerSideBlazor().AddCircuitOptions(option =>
|
||||||
{
|
{
|
||||||
option.DetailedErrors = true;
|
option.DetailedErrors = true;
|
||||||
|
}).AddHubOptions(option =>
|
||||||
|
{
|
||||||
|
option.MaximumReceiveMessageSize = 1024 * 1024 * 11;
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.WebHost.ConfigureKestrel(options =>
|
builder.WebHost.ConfigureKestrel(options =>
|
||||||
|
|
Loading…
Add table
Reference in a new issue