Added basic File upload and storage controllers, views, and models
This commit is contained in:
parent
71da65793e
commit
878810f1b8
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,4 +9,5 @@ public class ApplicationDbContext(
|
||||||
{
|
{
|
||||||
public DbSet<FileBlock> FileBlocks { get; set; }
|
public DbSet<FileBlock> FileBlocks { get; set; }
|
||||||
public DbSet<FileHandle> FileHandles { get; set; }
|
public DbSet<FileHandle> FileHandles { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -16,4 +16,5 @@ public class FileBlock
|
||||||
public required byte[] Data { get; init; }
|
public required byte[] Data { get; init; }
|
||||||
|
|
||||||
public required FileHandle FileHandle { get; init; }
|
public required FileHandle FileHandle { get; init; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@ using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace FileStorageService.www.Data;
|
namespace FileStorageService.www.Data;
|
||||||
|
|
||||||
public class FileHandle(string name)
|
public class FileHandle
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; init; }
|
||||||
|
|
||||||
[MaxLength(64)]
|
[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>();
|
||||||
}
|
}
|
||||||
|
|
@ -11,8 +11,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
namespace FileStorageService.www.Data.Migrations
|
namespace FileStorageService.www.Data.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(ApplicationDbContext))]
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
[Migration("20250317003031_InitialFileModels")]
|
[Migration("20250317131631_FileHandleAndFileBlockMigration")]
|
||||||
partial class InitialFileModels
|
partial class FileHandleAndFileBlockMigration
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
|
@ -63,7 +63,7 @@ namespace FileStorageService.www.Data.Migrations
|
||||||
modelBuilder.Entity("FileStorageService.www.Data.FileBlock", b =>
|
modelBuilder.Entity("FileStorageService.www.Data.FileBlock", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("FileStorageService.www.Data.FileHandle", "FileHandle")
|
b.HasOne("FileStorageService.www.Data.FileHandle", "FileHandle")
|
||||||
.WithMany("Blocks")
|
.WithMany("FileBlocks")
|
||||||
.HasForeignKey("FileHandleId")
|
.HasForeignKey("FileHandleId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
@ -73,7 +73,7 @@ namespace FileStorageService.www.Data.Migrations
|
||||||
|
|
||||||
modelBuilder.Entity("FileStorageService.www.Data.FileHandle", b =>
|
modelBuilder.Entity("FileStorageService.www.Data.FileHandle", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Blocks");
|
b.Navigation("FileBlocks");
|
||||||
});
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
|
|
@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
namespace FileStorageService.www.Data.Migrations
|
namespace FileStorageService.www.Data.Migrations
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class InitialFileModels : Migration
|
public partial class FileHandleAndFileBlockMigration : Migration
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
|
@ -60,7 +60,7 @@ namespace FileStorageService.www.Data.Migrations
|
||||||
modelBuilder.Entity("FileStorageService.www.Data.FileBlock", b =>
|
modelBuilder.Entity("FileStorageService.www.Data.FileBlock", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("FileStorageService.www.Data.FileHandle", "FileHandle")
|
b.HasOne("FileStorageService.www.Data.FileHandle", "FileHandle")
|
||||||
.WithMany("Blocks")
|
.WithMany("FileBlocks")
|
||||||
.HasForeignKey("FileHandleId")
|
.HasForeignKey("FileHandleId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
@ -70,7 +70,7 @@ namespace FileStorageService.www.Data.Migrations
|
||||||
|
|
||||||
modelBuilder.Entity("FileStorageService.www.Data.FileHandle", b =>
|
modelBuilder.Entity("FileStorageService.www.Data.FileHandle", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Blocks");
|
b.Navigation("FileBlocks");
|
||||||
});
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
namespace FileStorageService.www.Models;
|
||||||
|
|
||||||
|
public class FileHandleListModel
|
||||||
|
{
|
||||||
|
public List<FileHandleModel> FileHandles { get; init; }
|
||||||
|
}
|
||||||
|
|
@ -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; }
|
||||||
|
}
|
||||||
|
|
@ -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; }
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,7 @@ builder.Services.AddDatabaseDeveloperPageExceptionFilter();
|
||||||
builder.Services.AddControllersWithViews();
|
builder.Services.AddControllersWithViews();
|
||||||
builder.Services.AddRazorPages();
|
builder.Services.AddRazorPages();
|
||||||
|
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
Loading…
Reference in New Issue