Created basic file block count checker, takes concurrency into account

This commit is contained in:
michael-bailey 2025-03-17 15:59:29 +00:00
parent c06a569aa2
commit 5d19a2188f
4 changed files with 135 additions and 44 deletions

View File

@ -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<IActionResult> 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<IActionResult> ViewSingleFile([DisallowNull] Guid? id)
private async Task<IActionResult> 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();
}
}

View File

@ -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<FileRepository>();
var app = builder.Build();

View File

@ -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<List<FileHandle>> GetAllFilesAsync()
{
return await context.FileHandles
.Include(e => e.FileBlocks).ToListAsync();
}
public async Task<FileHandle> GetFileAsync(Guid id)
{
return await context.FileHandles
.Include(e => e.FileBlocks).FirstAsync();
}
public Task<Guid?> TryNewFileAsync(string name, Stream reader)
{
var tcs = new TaskCompletionSource<Guid?>();
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<FileHandle> CreateFileBlocks(Stream reader, FileHandle fileHandle)
{
var blockNumber = 0;
var blocks = new List<FileBlock>();
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;
}
}

View File

@ -0,0 +1,13 @@
@model dynamic
@{
ViewBag.Title = "Sorry :(";
Layout = "_Layout";
}
<h2>Sorry - :(</h2>
<p>There wasn't enough space to upload your file.</p>
<p>There is a total limit of 1024 blocks of data.</p>
<p>And looks like yours would have gone over that limit</p>
<p>Appologies for the inconvenience.</p>