From 878810f1b84e1643307109624e7b7227ee3502a3 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Mon, 17 Mar 2025 13:59:36 +0000 Subject: [PATCH] Added basic File upload and storage controllers, views, and models --- .../Controllers/FilesController.cs | 107 ++++++++++++++++++ .../Data/ApplicationDbContext.cs | 1 + FileStorageService.www/Data/FileBlock.cs | 1 + FileStorageService.www/Data/FileHandle.cs | 8 +- ...leHandleAndFileBlockMigration.Designer.cs} | 8 +- ...131631_FileHandleAndFileBlockMigration.cs} | 2 +- .../ApplicationDbContextModelSnapshot.cs | 4 +- .../Models/FileHandleListModel.cs | 6 + .../Models/FileHandleModel.cs | 9 ++ FileStorageService.www/Models/NewFileModel.cs | 14 +++ FileStorageService.www/Program.cs | 1 + .../Views/Files/Index.cshtml | 35 ++++++ FileStorageService.www/Views/Files/New.cshtml | 20 ++++ .../Views/Files/SingleFile.cshtml | 12 ++ 14 files changed, 217 insertions(+), 11 deletions(-) create mode 100644 FileStorageService.www/Controllers/FilesController.cs rename FileStorageService.www/Data/Migrations/{20250317003031_InitialFileModels.Designer.cs => 20250317131631_FileHandleAndFileBlockMigration.Designer.cs} (92%) rename FileStorageService.www/Data/Migrations/{20250317003031_InitialFileModels.cs => 20250317131631_FileHandleAndFileBlockMigration.cs} (96%) create mode 100644 FileStorageService.www/Models/FileHandleListModel.cs create mode 100644 FileStorageService.www/Models/FileHandleModel.cs create mode 100644 FileStorageService.www/Models/NewFileModel.cs create mode 100644 FileStorageService.www/Views/Files/Index.cshtml create mode 100644 FileStorageService.www/Views/Files/New.cshtml create mode 100644 FileStorageService.www/Views/Files/SingleFile.cshtml diff --git a/FileStorageService.www/Controllers/FilesController.cs b/FileStorageService.www/Controllers/FilesController.cs new file mode 100644 index 0000000..3111474 --- /dev/null +++ b/FileStorageService.www/Controllers/FilesController.cs @@ -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 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 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 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); + } + } +} \ No newline at end of file diff --git a/FileStorageService.www/Data/ApplicationDbContext.cs b/FileStorageService.www/Data/ApplicationDbContext.cs index eab4a2d..b59155a 100644 --- a/FileStorageService.www/Data/ApplicationDbContext.cs +++ b/FileStorageService.www/Data/ApplicationDbContext.cs @@ -9,4 +9,5 @@ public class ApplicationDbContext( { public DbSet FileBlocks { get; set; } public DbSet FileHandles { get; set; } + } \ No newline at end of file diff --git a/FileStorageService.www/Data/FileBlock.cs b/FileStorageService.www/Data/FileBlock.cs index 0c61b75..1b6ebdc 100644 --- a/FileStorageService.www/Data/FileBlock.cs +++ b/FileStorageService.www/Data/FileBlock.cs @@ -16,4 +16,5 @@ public class FileBlock public required byte[] Data { get; init; } public required FileHandle FileHandle { get; init; } + } diff --git a/FileStorageService.www/Data/FileHandle.cs b/FileStorageService.www/Data/FileHandle.cs index 0a72f26..d5f3006 100644 --- a/FileStorageService.www/Data/FileHandle.cs +++ b/FileStorageService.www/Data/FileHandle.cs @@ -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 Blocks { get; } = []; + public ICollection FileBlocks { get; } = new List(); } \ No newline at end of file diff --git a/FileStorageService.www/Data/Migrations/20250317003031_InitialFileModels.Designer.cs b/FileStorageService.www/Data/Migrations/20250317131631_FileHandleAndFileBlockMigration.Designer.cs similarity index 92% rename from FileStorageService.www/Data/Migrations/20250317003031_InitialFileModels.Designer.cs rename to FileStorageService.www/Data/Migrations/20250317131631_FileHandleAndFileBlockMigration.Designer.cs index 0f903b5..8254fd9 100644 --- a/FileStorageService.www/Data/Migrations/20250317003031_InitialFileModels.Designer.cs +++ b/FileStorageService.www/Data/Migrations/20250317131631_FileHandleAndFileBlockMigration.Designer.cs @@ -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 { /// 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 } diff --git a/FileStorageService.www/Data/Migrations/20250317003031_InitialFileModels.cs b/FileStorageService.www/Data/Migrations/20250317131631_FileHandleAndFileBlockMigration.cs similarity index 96% rename from FileStorageService.www/Data/Migrations/20250317003031_InitialFileModels.cs rename to FileStorageService.www/Data/Migrations/20250317131631_FileHandleAndFileBlockMigration.cs index f0ab9cd..540b729 100644 --- a/FileStorageService.www/Data/Migrations/20250317003031_InitialFileModels.cs +++ b/FileStorageService.www/Data/Migrations/20250317131631_FileHandleAndFileBlockMigration.cs @@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore.Migrations; namespace FileStorageService.www.Data.Migrations { /// - public partial class InitialFileModels : Migration + public partial class FileHandleAndFileBlockMigration : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) diff --git a/FileStorageService.www/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/FileStorageService.www/Data/Migrations/ApplicationDbContextModelSnapshot.cs index 10a654a..b0da111 100644 --- a/FileStorageService.www/Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/FileStorageService.www/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -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 } diff --git a/FileStorageService.www/Models/FileHandleListModel.cs b/FileStorageService.www/Models/FileHandleListModel.cs new file mode 100644 index 0000000..ef6eea1 --- /dev/null +++ b/FileStorageService.www/Models/FileHandleListModel.cs @@ -0,0 +1,6 @@ +namespace FileStorageService.www.Models; + +public class FileHandleListModel +{ + public List FileHandles { get; init; } +} \ No newline at end of file diff --git a/FileStorageService.www/Models/FileHandleModel.cs b/FileStorageService.www/Models/FileHandleModel.cs new file mode 100644 index 0000000..acdcbbb --- /dev/null +++ b/FileStorageService.www/Models/FileHandleModel.cs @@ -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; } +} \ No newline at end of file diff --git a/FileStorageService.www/Models/NewFileModel.cs b/FileStorageService.www/Models/NewFileModel.cs new file mode 100644 index 0000000..f67c52e --- /dev/null +++ b/FileStorageService.www/Models/NewFileModel.cs @@ -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; } +} \ No newline at end of file diff --git a/FileStorageService.www/Program.cs b/FileStorageService.www/Program.cs index 56073aa..a74cfef 100644 --- a/FileStorageService.www/Program.cs +++ b/FileStorageService.www/Program.cs @@ -16,6 +16,7 @@ builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddControllersWithViews(); builder.Services.AddRazorPages(); + var app = builder.Build(); // Configure the HTTP request pipeline. diff --git a/FileStorageService.www/Views/Files/Index.cshtml b/FileStorageService.www/Views/Files/Index.cshtml new file mode 100644 index 0000000..d8996ee --- /dev/null +++ b/FileStorageService.www/Views/Files/Index.cshtml @@ -0,0 +1,35 @@ +@model FileHandleListModel + +@{ + ViewData["Title"] = "Files"; + Layout = "_Layout"; +} + +

All Files

+ +@if (!Model.FileHandles.Any()) +{ +

No files available

+} +else +{ + + + + + + + + + + @foreach(var file in Model.FileHandles) + { + + + + + + } + +
NameBlock CountLinks
@file.FileName@file.BlockCountView...
+} \ No newline at end of file diff --git a/FileStorageService.www/Views/Files/New.cshtml b/FileStorageService.www/Views/Files/New.cshtml new file mode 100644 index 0000000..c5672cf --- /dev/null +++ b/FileStorageService.www/Views/Files/New.cshtml @@ -0,0 +1,20 @@ +@model NewFileModel + +@{ + ViewBag.Title = "title"; + Layout = "_Layout"; +} + +

New File

+ +
+
+ + +
+
+ + +
+ +
\ No newline at end of file diff --git a/FileStorageService.www/Views/Files/SingleFile.cshtml b/FileStorageService.www/Views/Files/SingleFile.cshtml new file mode 100644 index 0000000..653eaf8 --- /dev/null +++ b/FileStorageService.www/Views/Files/SingleFile.cshtml @@ -0,0 +1,12 @@ +@model FileHandleModel; + +@{ + +} + +

File - @Model.FileName

+ +
    +
  • File Name: @Model.FileName
  • +
  • Block Count: @Model.BlockCount
  • +
\ No newline at end of file