This commit is contained in:
femsci 2023-03-22 11:32:04 +01:00
parent 61725b4d07
commit 5bf51d108c
Signed by: femsci
GPG key ID: 08F7911F0E650C67
5 changed files with 214 additions and 28 deletions

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using MeowStash.Services;
using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -11,8 +12,15 @@ namespace MeowStash.Controllers;
[ApiController] [ApiController]
public class ResourceController : ControllerBase public class ResourceController : ControllerBase
{ {
[HttpPost("add")] public ResourceController(FileIOService io)
public async Task<IActionResult> AddFiles([FromForm] IFormFileCollection files) {
_io = io;
}
private readonly FileIOService _io;
[HttpPost("upload")]
public async Task<IActionResult> UploadFiles([FromForm] IFormFileCollection files)
{ {
foreach (var file in files) foreach (var file in files)
{ {
@ -23,4 +31,12 @@ public class ResourceController : ControllerBase
return NoContent(); return NoContent();
} }
[HttpPost("upload/fragment/{id}")]
public async Task<IActionResult> UploadFileFragment([FromRoute] int uploadId, [FromBody] byte[] dataPacket)
{
using var buffer = new MemoryStream(dataPacket);
await _io.WritePartialFile(uploadId, buffer);
return NoContent();
}
} }

View file

@ -7,36 +7,108 @@
<InputFile OnChange="@SendFiles" multiple /><br /> <InputFile OnChange="@SendFiles" multiple /><br />
<span class="@_umsg_class">@_umsg</span>
@if (_uploadData.Any())
{
<div class="uploads">
@foreach (var item in _uploadData)
{
<div class="upload-item">
<div class="upload-item-data">
<span class="upload-item-name">@item.Key.Name</span>
</div>
<div class="upload-progress">
<span class="upload-progress-value @GetUploadProgressClassesFor(item.Value.Item1)">@item.Value.Item1%</span>
<span class="upload-progress-bytes">@item.Value.Item2</span>
<div class="upload-progress-bar-container">
<div class="upload-progress-bar @GetUploadProgressClassesFor(item.Value.Item1)"
style="width: @($"{item.Value.Item1}%");"></div>
</div>
</div>
</div>
}
</div>
}
@code { @code {
private string _umsg = "", _umsg_class = "text-hidden";
private string GetUploadProgressClassesFor(float percent)
{
return percent switch
{
(< 5f) => "upload-progress-low",
(> 5f) and (< 50f) => "upload-progress-submedial",
(> 50f) and (< 95f) => "upload-progress-supermedial",
(> 95f) and (< 100f) => "upload-progress-nearly-done",
100f or (> 100f) => "upload-progress-done",
_ => "upload-progress-invalid"
};
}
private System.Collections.Concurrent.ConcurrentDictionary<IBrowserFile, (float, long)> _uploadData = new();
private CancellationTokenSource _cancel = null!;
private async Task SendFiles(InputFileChangeEventArgs e) private async Task SendFiles(InputFileChangeEventArgs e)
{ {
Console.WriteLine($"Files: {e.FileCount}"); if (_cancel != null)
{
_cancel.Cancel();
_cancel.Dispose();
}
_cancel = new();
var files = e.GetMultipleFiles(e.FileCount); var files = e.GetMultipleFiles(e.FileCount);
_umsg_class = "text-green"; _uploadData = new(files.Count, files.ToDictionary(f => f, _ => (0f, 0L)), null);
int counter = 1;
foreach (var file in files)
{
_umsg = $"Uploading {counter}/{e.FileCount} files...";
StateHasChanged();
Console.WriteLine($"Fos: /tmp/uploads/{file.Name}");
if (!System.IO.Directory.Exists("/tmp/uploads")) if (!System.IO.Directory.Exists("/tmp/uploads"))
{ {
System.IO.Directory.CreateDirectory("/tmp/uploads"); System.IO.Directory.CreateDirectory("/tmp/uploads");
} }
Console.WriteLine($"fosse");
var tasks = new List<Task>();
foreach (var file in files)
{
tasks.Add(Task.Run(async () =>
{
await using var fos = System.IO.File.OpenWrite($"/tmp/uploads/{file.Name}"); await using var fos = System.IO.File.OpenWrite($"/tmp/uploads/{file.Name}");
Console.WriteLine("Fos opened");
await file.OpenReadStream(1024 * 1024 * 10).CopyToAsync(fos); var stream = file.OpenReadStream(1024 * 1024 * 1024, _cancel.Token);
Console.WriteLine($"File: /tmp/uploads/{file.Name} written..."); int read;
++counter;
byte[] buffer = new byte[16 * 1024];
while ((read = await stream.ReadAsync(buffer, 0, buffer.Length, _cancel.Token)) > 0)
{
if (_cancel.IsCancellationRequested)
{
stream.Close();
return;
} }
_umsg = "Done!"; await fos.WriteAsync(buffer, 0, read);
long totalRead = _uploadData[file].Item2;
totalRead += read;
float percent = float.Round(((float)totalRead / (float)file.Size * 100f), 2);
_uploadData[file] = (percent, totalRead);
await InvokeAsync(StateHasChanged);
await Task.Delay(500, _cancel.Token);
}
stream.Close();
Console.WriteLine($"File: /tmp/uploads/{file.Name} written...");
await InvokeAsync(StateHasChanged);
}));
}
await Task.WhenAll(tasks);
StateHasChanged(); StateHasChanged();
} }
} }

View file

@ -1,19 +1,13 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages(); builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor(); builder.Services.AddServerSideBlazor();
var app = builder.Build(); var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment()) if (!app.Environment.IsDevelopment())
{ {
app.UseExceptionHandler("/Error"); app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts(); app.UseHsts();
} }

23
Services/FileIOService.cs Normal file
View file

@ -0,0 +1,23 @@
namespace MeowStash.Services;
public class FileIOService
{
public async Task WriteFullFile(string location, byte[] data)
{
await File.WriteAllBytesAsync(location, data);
}
public async Task WriteFullFile(string location, Stream data)
{
using var stream = File.OpenWrite(location);
await data.CopyToAsync(stream);
stream.Close();
}
public async Task WritePartialFile(int id, Stream data)
{
using var stream = File.Open($"/tmp/{id}", FileMode.Append);
await data.CopyToAsync(stream);
stream.Close();
}
}

View file

@ -86,3 +86,84 @@ a,
.blazor-error-boundary::after { .blazor-error-boundary::after {
content: "An error has occurred."; content: "An error has occurred.";
} }
.upload-item {
display: block;
}
.upload-item-name {
color: lightgray;
}
.upload-progress {
display: block;
width: 100%;
}
.upload-progress-value {
display: block;
}
.upload-progress-bytes {
color: lightgray;
display: block;
}
.upload-progress-bar-container {
width: 80%;
height: 30px;
background-color: gray;
border-radius: 10px;
}
span.upload-progress-low {
color: lightcoral;
}
span.upload-progress-submedial {
color: yellow;
}
span.upload-progress-supermedial {
color: greenyellow;
}
span.upload-progress-nearly-done {
color: limegreen;
}
span.upload-progress-done {
color: green;
}
span.upload-progress-invalid {
color: darkgray;
}
.upload-progress-bar {
height: 100%;
}
.upload-progress-bar.upload-progress-low {
background-color: lightcoral;
}
.upload-progress-bar.upload-progress-submedial {
background-color: yellow;
}
.upload-progress-bar.upload-progress-supermedial {
background-color: greenyellow;
}
.upload-progress-bar.upload-progress-nearly-done {
background-color: limegreen;
}
.upload-progress-bar.upload-progress-done {
background-color: green;
}
.upload-progress-bar.upload-progress-invalid {
background-color: darkgray;
}