Started re-implementing file uploader
This commit is contained in:
parent
35965be69a
commit
ce0b38d532
2 changed files with 155 additions and 0 deletions
51
LANCommander/Controllers/Api/UploadController.cs
Normal file
51
LANCommander/Controllers/Api/UploadController.cs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
using LANCommander.Models;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace LANCommander.Controllers.Api
|
||||||
|
{
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class UploadController : ControllerBase
|
||||||
|
{
|
||||||
|
private const string UploadDirectory = "Upload";
|
||||||
|
|
||||||
|
[HttpPost("Init")]
|
||||||
|
public string Init()
|
||||||
|
{
|
||||||
|
var key = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
if (!Directory.Exists(UploadDirectory))
|
||||||
|
Directory.CreateDirectory(UploadDirectory);
|
||||||
|
|
||||||
|
if (!System.IO.File.Exists(Path.Combine(UploadDirectory, key)))
|
||||||
|
System.IO.File.Create(Path.Combine(UploadDirectory, key)).Close();
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("Chunk")]
|
||||||
|
public async Task Chunk([FromForm] ChunkUpload chunk)
|
||||||
|
{
|
||||||
|
var filePath = Path.Combine(UploadDirectory, chunk.Key.ToString());
|
||||||
|
|
||||||
|
if (!System.IO.File.Exists(filePath))
|
||||||
|
throw new Exception("Destination file not initialized.");
|
||||||
|
|
||||||
|
Request.EnableBuffering();
|
||||||
|
|
||||||
|
using (var ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
await chunk.File.CopyToAsync(ms);
|
||||||
|
|
||||||
|
var data = ms.ToArray();
|
||||||
|
|
||||||
|
using (var fs = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.None))
|
||||||
|
{
|
||||||
|
fs.Position = chunk.Start;
|
||||||
|
fs.Write(data, 0, data.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
104
LANCommander/Pages/Games/Archives/Upload.razor
Normal file
104
LANCommander/Pages/Games/Archives/Upload.razor
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
@page "/Games/{id:guid}/Archives/Upload"
|
||||||
|
@using System.Net;
|
||||||
|
@inject HttpClient HttpClient
|
||||||
|
@inject NavigationManager Navigator
|
||||||
|
|
||||||
|
<MudFileUpload T="IBrowserFile" FilesChanged="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" />
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter] public Guid Id { get; set; }
|
||||||
|
|
||||||
|
IBrowserFile File { get; set; }
|
||||||
|
|
||||||
|
const int ChunkSize = 1024 * 1024 * 1;
|
||||||
|
|
||||||
|
int Progress = 0;
|
||||||
|
bool Uploading = false;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
HttpClient.BaseAddress = new Uri(Navigator.BaseUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FileSelected(IBrowserFile file)
|
||||||
|
{
|
||||||
|
File = 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
long uploadedBytes = 0;
|
||||||
|
long totalBytes = File.Size;
|
||||||
|
|
||||||
|
using (var stream = File.OpenReadStream(long.MaxValue))
|
||||||
|
{
|
||||||
|
Uploading = true;
|
||||||
|
|
||||||
|
while (Uploading)
|
||||||
|
{
|
||||||
|
byte[] chunk;
|
||||||
|
|
||||||
|
if (totalBytes - uploadedBytes < ChunkSize)
|
||||||
|
chunk = new byte[totalBytes - uploadedBytes];
|
||||||
|
else
|
||||||
|
chunk = new byte[ChunkSize];
|
||||||
|
|
||||||
|
await stream.ReadAsync(chunk, 0, chunk.Length);
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue