Added basic File upload and storage controllers, views, and models

This commit is contained in:
michael-bailey 2025-03-17 13:59:36 +00:00
parent 71da65793e
commit 878810f1b8
14 changed files with 217 additions and 11 deletions

View File

@ -0,0 +1,107 @@
using System.Diagnostics.CodeAnalysis;
using FileStorageService.www.Data;
using FileStorageService.www.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Build.Framework;
using Microsoft.EntityFrameworkCore;
namespace FileStorageService.www.Controllers;
public class FilesController(ApplicationDbContext context) : Controller
{
// GET
public async Task<IActionResult> Index(Guid? id)
{
if (id != null)
{
return await ViewSingleFile(id);
}
var handles = await context.FileHandles
.Include(e => e.FileBlocks)
.Select(e => new FileHandleModel
{
FileName = e.Name,
BlockCount = e.FileBlocks.Count,
Id = e.Id,
})
.ToListAsync();
var model = new FileHandleListModel
{
FileHandles = handles
};
return View(model);
}
private async Task<IActionResult> ViewSingleFile([DisallowNull] Guid? id)
{
var fileHandle = await context.FileHandles
.Include(e => e.FileBlocks)
.FirstAsync(e => e.Id ==id);
var model = new FileHandleModel
{
FileName = fileHandle.Name,
BlockCount = fileHandle.FileBlocks.Count,
Id = fileHandle.Id,
};
return View("SingleFile", model);
}
public IActionResult New()
{
return View(new NewFileModel
{
Name = "",
FileContents = null!
});
}
[HttpPost]
public async Task<IActionResult> New(NewFileModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState.Values.SelectMany(v => v.Errors));
}
var handle = new FileHandle
{
Name = model.Name
};
context.Add(handle);
var readStream = model.FileContents.OpenReadStream();
await CreateFileBlocks(handle, readStream);
await context.SaveChangesAsync();
return Redirect($"/Files/{handle.Id}");
}
private async Task CreateFileBlocks(FileHandle fileHandle, Stream reader)
{
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);
}
}
}

View File

@ -9,4 +9,5 @@ public class ApplicationDbContext(
{
public DbSet<FileBlock> FileBlocks { get; set; }
public DbSet<FileHandle> FileHandles { get; set; }
}

View File

@ -16,4 +16,5 @@ public class FileBlock
public required byte[] Data { get; init; }
public required FileHandle FileHandle { get; init; }
}

View File

@ -2,12 +2,12 @@ using System.ComponentModel.DataAnnotations;
namespace FileStorageService.www.Data;
public class FileHandle(string name)
public class FileHandle
{
public Guid Id { get; set; }
public Guid Id { get; init; }
[MaxLength(64)]
public string Name { get; set; } = name;
public required string Name { get; set; }
public ICollection<FileBlock> Blocks { get; } = [];
public ICollection<FileBlock> FileBlocks { get; } = new List<FileBlock>();
}

View File

@ -11,8 +11,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace FileStorageService.www.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20250317003031_InitialFileModels")]
partial class InitialFileModels
[Migration("20250317131631_FileHandleAndFileBlockMigration")]
partial class FileHandleAndFileBlockMigration
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -63,7 +63,7 @@ namespace FileStorageService.www.Data.Migrations
modelBuilder.Entity("FileStorageService.www.Data.FileBlock", b =>
{
b.HasOne("FileStorageService.www.Data.FileHandle", "FileHandle")
.WithMany("Blocks")
.WithMany("FileBlocks")
.HasForeignKey("FileHandleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
@ -73,7 +73,7 @@ namespace FileStorageService.www.Data.Migrations
modelBuilder.Entity("FileStorageService.www.Data.FileHandle", b =>
{
b.Navigation("Blocks");
b.Navigation("FileBlocks");
});
#pragma warning restore 612, 618
}

View File

@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace FileStorageService.www.Data.Migrations
{
/// <inheritdoc />
public partial class InitialFileModels : Migration
public partial class FileHandleAndFileBlockMigration : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -60,7 +60,7 @@ namespace FileStorageService.www.Data.Migrations
modelBuilder.Entity("FileStorageService.www.Data.FileBlock", b =>
{
b.HasOne("FileStorageService.www.Data.FileHandle", "FileHandle")
.WithMany("Blocks")
.WithMany("FileBlocks")
.HasForeignKey("FileHandleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
@ -70,7 +70,7 @@ namespace FileStorageService.www.Data.Migrations
modelBuilder.Entity("FileStorageService.www.Data.FileHandle", b =>
{
b.Navigation("Blocks");
b.Navigation("FileBlocks");
});
#pragma warning restore 612, 618
}

View File

@ -0,0 +1,6 @@
namespace FileStorageService.www.Models;
public class FileHandleListModel
{
public List<FileHandleModel> FileHandles { get; init; }
}

View File

@ -0,0 +1,9 @@
namespace FileStorageService.www.Models;
public class FileHandleModel
{
public required string FileName { get; init; }
public required int BlockCount { get; init; }
public required Guid Id { get; init; }
}

View File

@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace FileStorageService.www.Models;
public class NewFileModel
{
[MinLength(4)]
[Required]
public string Name { get; init; }
[Required]
public IFormFile FileContents { get; init; }
}

View File

@ -16,6 +16,7 @@ builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.

View File

@ -0,0 +1,35 @@
@model FileHandleListModel
@{
ViewData["Title"] = "Files";
Layout = "_Layout";
}
<h2>All Files</h2>
@if (!Model.FileHandles.Any())
{
<p>No files available</p>
}
else
{
<table class="table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Block Count</th>
<th scope="col">Links</th>
</tr>
</thead>
<tbody>
@foreach(var file in Model.FileHandles)
{
<tr>
<th scope="row">@file.FileName</th>
<th scope="row">@file.BlockCount</th>
<th scope="row"><a asp-controller="Files" asp-action="Index" asp-route-id="@file.Id">View...</a></th>
</tr>
}
</tbody>
</table>
}

View File

@ -0,0 +1,20 @@
@model NewFileModel
@{
ViewBag.Title = "title";
Layout = "_Layout";
}
<h2>New File</h2>
<form asp-controller="Files" asp-action="New" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label asp-for="Name">Name</label>
<input asp-for="Name">
</div>
<div class="mb-3">
<label asp-for="FileContents">File</label>
<input asp-for="FileContents">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>

View File

@ -0,0 +1,12 @@
@model FileHandleModel;
@{
}
<h2>File - @Model.FileName</h2>
<ul class="list-group">
<li class="list-group-item">File Name: @Model.FileName</li>
<li class="list-group-item">Block Count: @Model.BlockCount</li>
</ul>