From 5d19a2188f1db0e50a527e77dfce8325f0f296a7 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Mon, 17 Mar 2025 15:59:29 +0000 Subject: [PATCH] Created basic file block count checker, takes concurrency into account --- .../Controllers/FilesController.cs | 69 +++++--------- FileStorageService.www/Program.cs | 4 + .../Repositories/FileRepository.cs | 93 +++++++++++++++++++ .../Views/Files/NotEnoughSpace.cshtml | 13 +++ 4 files changed, 135 insertions(+), 44 deletions(-) create mode 100644 FileStorageService.www/Repositories/FileRepository.cs create mode 100644 FileStorageService.www/Views/Files/NotEnoughSpace.cshtml diff --git a/FileStorageService.www/Controllers/FilesController.cs b/FileStorageService.www/Controllers/FilesController.cs index d2b3532..1b63ef8 100644 --- a/FileStorageService.www/Controllers/FilesController.cs +++ b/FileStorageService.www/Controllers/FilesController.cs @@ -1,45 +1,46 @@ using System.Diagnostics.CodeAnalysis; using FileStorageService.www.Data; using FileStorageService.www.Models; +using FileStorageService.www.Repositories; using Microsoft.AspNetCore.Mvc; using Microsoft.Build.Framework; using Microsoft.EntityFrameworkCore; namespace FileStorageService.www.Controllers; -public class FilesController(ApplicationDbContext context) : Controller +public class FilesController( + ApplicationDbContext context, + FileRepository fileRepository) : Controller { // GET public async Task Index(Guid? id) { - if (id != null) + if (id is not null) { - return await ViewSingleFile(id); + return await ViewSingleFile((Guid)id); } - - var handles = await context.FileHandles - .Include(e => e.FileBlocks) - .Select(e => new FileHandleModel + + var fileHandles = await fileRepository.GetAllFilesAsync(); + + var handles = fileHandles.Select(e => new FileHandleModel { FileName = e.Name, BlockCount = e.FileBlocks.Count, Id = e.Id, }) - .ToListAsync(); + .ToList(); var model = new FileHandleListModel { FileHandles = handles }; - + return View(model); } - private async Task ViewSingleFile([DisallowNull] Guid? id) + private async Task ViewSingleFile(Guid id) { - var fileHandle = await context.FileHandles - .Include(e => e.FileBlocks) - .FirstAsync(e => e.Id ==id); + var fileHandle = await fileRepository.GetFileAsync(id); var model = new FileHandleModel { @@ -68,40 +69,20 @@ public class FilesController(ApplicationDbContext context) : Controller return BadRequest(ModelState.Values.SelectMany(v => v.Errors)); } - var handle = new FileHandle - { - Name = model.Name - }; - context.Add(handle); + var name = model.Name; + var stream = model.FileContents.OpenReadStream(); - var readStream = model.FileContents.OpenReadStream(); - - await CreateFileBlocks(handle, readStream); - - await context.SaveChangesAsync(); - - return RedirectToAction("Index", "Files", new{ id = handle.Id}); + var fileHandleId = await fileRepository.TryNewFileAsync(name, stream); + + if (fileHandleId != null) + return RedirectToAction("Index", "Files", new { id = fileHandleId }); + + return RedirectToAction("NotEnoughSpace"); } - private async Task CreateFileBlocks(FileHandle fileHandle, Stream reader) + public IActionResult NotEnoughSpace() { - - var blockNumber = 0; - - var buffer = new byte[1024]; - - while (await reader.ReadAsync(buffer) > 0) - { - var block = new FileBlock - { - BlockNumber = blockNumber++, - Data = buffer, - FileHandle = fileHandle - }; - - // context.Add(block); - - fileHandle.FileBlocks.Add(block); - } + return View(); } + } \ No newline at end of file diff --git a/FileStorageService.www/Program.cs b/FileStorageService.www/Program.cs index a74cfef..9748fa1 100644 --- a/FileStorageService.www/Program.cs +++ b/FileStorageService.www/Program.cs @@ -1,6 +1,8 @@ +using FileStorageService.www; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using FileStorageService.www.Data; +using FileStorageService.www.Repositories; var builder = WebApplication.CreateBuilder(args); @@ -16,6 +18,8 @@ builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddControllersWithViews(); builder.Services.AddRazorPages(); +builder.Services.AddScoped(); + var app = builder.Build(); diff --git a/FileStorageService.www/Repositories/FileRepository.cs b/FileStorageService.www/Repositories/FileRepository.cs new file mode 100644 index 0000000..5fea9fc --- /dev/null +++ b/FileStorageService.www/Repositories/FileRepository.cs @@ -0,0 +1,93 @@ +using FileStorageService.www.Data; +using Microsoft.EntityFrameworkCore; + +namespace FileStorageService.www.Repositories; + +public class FileRepository(ApplicationDbContext context) +{ + private const int MAX_BLOCKS = 1024; + + private Queue<(string, Stream)> creationQueue = new(); + private Lock countLock = new(); + + public async Task> GetAllFilesAsync() + { + return await context.FileHandles + .Include(e => e.FileBlocks).ToListAsync(); + } + + public async Task GetFileAsync(Guid id) + { + return await context.FileHandles + .Include(e => e.FileBlocks).FirstAsync(); + } + + public Task TryNewFileAsync(string name, Stream reader) + { + var tcs = new TaskCompletionSource(); + + lock (creationQueue) + { + creationQueue.Enqueue((name, reader)); + } + + Task.Run(async () => + { + + Stream stream; + string name; + + lock (creationQueue) + { + (name, stream) = creationQueue.Dequeue(); + } + + var fileHandle = new FileHandle + { + Name = name + }; + + var handle = await CreateFileBlocks(stream, fileHandle); + var currentFileCount = handle.FileBlocks.Count(); + + lock (countLock) + { + var count = context.FileBlocks.Count(); + + if (count+currentFileCount > MAX_BLOCKS) + { + tcs.SetResult(null); + return; + } + + context.Add(fileHandle); + context.SaveChangesAsync(); + + tcs.SetResult(fileHandle.Id); + } + }); + + return tcs.Task; + } + + + private async Task CreateFileBlocks(Stream reader, FileHandle fileHandle) + { + var blockNumber = 0; + var blocks = new List(); + var buffer = new byte[1024]; + + while (await reader.ReadAsync(buffer) > 0) + { + var block = new FileBlock + { + BlockNumber = blockNumber++, + Data = buffer, + FileHandle = fileHandle + }; + fileHandle.FileBlocks.Add(block); + } + + return fileHandle; + } +} \ No newline at end of file diff --git a/FileStorageService.www/Views/Files/NotEnoughSpace.cshtml b/FileStorageService.www/Views/Files/NotEnoughSpace.cshtml new file mode 100644 index 0000000..77baba1 --- /dev/null +++ b/FileStorageService.www/Views/Files/NotEnoughSpace.cshtml @@ -0,0 +1,13 @@ +@model dynamic + +@{ + ViewBag.Title = "Sorry :("; + Layout = "_Layout"; +} + +

Sorry - :(

+ +

There wasn't enough space to upload your file.

+

There is a total limit of 1024 blocks of data.

+

And looks like yours would have gone over that limit

+

Appologies for the inconvenience.