Created basic file block count checker, takes concurrency into account
This commit is contained in:
parent
c06a569aa2
commit
5d19a2188f
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
Loading…
Reference in New Issue