FileHub.Core
1.0.1
dotnet add package FileHub.Core --version 1.0.1
NuGet\Install-Package FileHub.Core -Version 1.0.1
<PackageReference Include="FileHub.Core" Version="1.0.1" />
<PackageVersion Include="FileHub.Core" Version="1.0.1" />
<PackageReference Include="FileHub.Core" />
paket add FileHub.Core --version 1.0.1
#r "nuget: FileHub.Core, 1.0.1"
#:package FileHub.Core@1.0.1
#addin nuget:?package=FileHub.Core&version=1.0.1
#tool nuget:?package=FileHub.Core&version=1.0.1
FileHub.Core
Sandboxed file-manager building blocks for ASP.NET Core: a pluggable
storage abstraction, path/traversal security, chunked upload session
management, image thumbnail generation, Office-document-to-PDF
conversion, and a drop-in MVC widget (Html.FileManager()) that renders
the full browse/upload/download/preview UI from one Razor call.
This package is the reusable engine behind FileHub.WebDemo, a full working ASP.NET Core MVC file manager built on top of it — that project is the best reference for wiring these pieces into your own app.
Install
dotnet add package FileHub.Core
Requires an ASP.NET Core host — the package references
Microsoft.AspNetCore.App (used for MIME-type detection, MVC controller
hosting, and static web assets).
What's in the box
Abstractions/—IFileSystemProvider,FileSystemItem: the storage abstraction. Implement this interface for a different backend (Azure Blob, S3, ...);PhysicalFileSystemProvideris the built-in local-disk implementation.Security/—PathValidator(traversal / absolute-path / symlink-escape protection) andFilenameSanitizer. Every provider operation should resolve throughPathValidatorbefore touching disk.Roots/—RootFolder,RootFolderOptions,RootFolderProvider: binds configuration into runtime root definitions (name, physical path, permissions, upload limits, extension allow/deny lists), and accepts runtime registrations too viaIRootFolderProvider.GetOrRegister— used byHtml.FileManager()'s inline configuration.Permissions/—PermissionFlags(browse/upload/download/create/rename/ move/copy/delete),PermissionFlagsParser(parses permission-name lists into flags).Uploads/—UploadSessionManager: chunk staging + assembly for large uploads.Thumbnails/—ThumbnailService: SkiaSharp-based image thumbnails, disk-cached, keyed by root + path + last-modified.Conversion/—IOfficeConversionService/LibreOfficeConversionService: shells out tosoffice --headless --convert-to pdf, disk-cached, per-cache-key locking. Requires LibreOffice installed on the host machine (configure its path viaLibreOfficeOptions.ExecutablePath).Preview/—PreviewFileClassifier: which extensions need office conversion vs. streaming as-is.Extensions/—AddFileHubCore(): registersIRootFolderProvider,UploadSessionManager,ThumbnailService, andIOfficeConversionServicein one call, using theApp_Data/.uploads,App_Data/.cache/thumbnails, andApp_Data/.cache/office-pdfpaths under the given content root. Its optionalsharedRootPathargument is the base directory a relative.FolderPath(...)inHtml.FileManager()resolves against (defaults to the content root when omitted). Framework-agnostic (IServiceCollectiononly) — usable outside MVC.Widget/Mvc/— the ASP.NET Core MVC front end, kept separate from the backend folders above so a future non-MVC host (Blazor, Minimal APIs) could reuse the backend without carrying MVC-specific types:Controllers/—FileManagerController, the/filemanager/*API (browse/upload/download/preview/CRUD), registered viaAddFileHubMvc().Extensions/—Html.FileManager()(returns a fluent builder) andAddFileHubMvc()(registers the controller as an application part on the host'sIMvcBuilder).Rendering/—FileManagerBuilder(the fluent.FolderPath(...).Permissions(PermissionFlags...)...object; computes a stable identifier from its configuration and registers it viaIRootFolderProvider.GetOrRegisterso the physical path never reaches the browser) andFileManagerMarkup(builds the widget's HTML directly in C#, no.cshtmlinvolved).
wwwroot/js/filemanager/,wwwroot/css/filemanager.css,wwwroot/lib/pdfjs/— the vanilla-JS UI and vendored PDF.js viewer, kept at the project root (not underWidget/) since ASP.NET Core's static web asset convention only auto-discovers a top-levelwwwroot/, served as static web assets at_content/FileHub.Core/....
Quick start
using FileHub.Core.Extensions;
using FileHub.Core.Widget.Mvc.Extensions;
builder.Services.AddControllersWithViews().AddFileHubMvc();
builder.Services.AddFileHubCore(builder.Configuration, builder.Environment.ContentRootPath);
var app = builder.Build();
app.UseFileHubStaticFiles();
app.UseRouting();
app.MapControllers();
AddFileHubCore() registers IRootFolderProvider (bound from the
FileHub:Roots configuration section), UploadSessionManager,
ThumbnailService, and IOfficeConversionService — all as singletons,
using App_Data/.uploads, App_Data/.cache/thumbnails, and
App_Data/.cache/office-pdf under the given content root, and
LibreOffice:ExecutablePath for the conversion service. For a different
cache/upload location, a different lifetime, or a different
IOfficeConversionService implementation, register the services
individually instead — see src/FileHub.Core/Extensions/ServiceCollectionExtensions.cs
for exactly what it does.
app.UseFileHubStaticFiles() calls UseStaticFiles() with the extra MIME
mappings the vendored PDF.js viewer's character-map (.bcmap) and
localization (.ftl) files need — neither is in ASP.NET Core's default
extension map, so without these StaticFileMiddleware 404s them instead
of falling back to application/octet-stream. Use this in place of your
own app.UseStaticFiles() call, or add these two mappings to your
existing FileExtensionContentTypeProvider if you already configure one.
@* Anywhere in a Razor view *@
@(Html.FileManager()
.FolderPath("App_Data/MyFolder")
.Permissions(PermissionFlags.CanBrowse | PermissionFlags.CanUpload | PermissionFlags.CanDownload | PermissionFlags.CanCreate | PermissionFlags.CanRename | PermissionFlags.CanMove | PermissionFlags.CanCopy | PermissionFlags.CanDelete)
.AllowedExtensions([ ".pdf", ".png", ".jpg" ])
.MaxUploadSizeMB(100))
The outer @( ... ) is required — Razor's implicit-expression parser doesn't
continue an expression across a newline into a leading-dot method call, so
an unwrapped @Html.FileManager() followed by .FolderPath(...) on the
next line silently never calls the chained methods.
Configuration is entirely inline, at the call site — no appsettings.json
root configuration needed for widget-rendered roots (binding roots under a
FileHub:Roots configuration section, as shown above, is still supported
for named, host-configured roots). FolderPath is required;
Permissions, AllowedExtensions, and MaxUploadSizeMB are optional and
default to "all permissions", "deny only the built-in blocked-executable
list", and 100 MB respectively. A relative FolderPath resolves against
AddFileHubCore's sharedRootPath (or its contentRootPath, if
sharedRootPath wasn't given) — an absolute path is used as-is.
The full physical path never reaches the browser: the widget computes a
stable, opaque identifier from the full configuration and registers it
server-side (via IRootFolderProvider.GetOrRegister) — only that
identifier is sent on API calls. The rendered markup does include one
piece of the path, though: the resolved folder's last path segment (e.g.
FolderPath("D:\InternalShares\ClientName-Contracts") renders the label
ClientName-Contracts) is written as data-root-label, so the UI can
show a human-readable root name instead of the opaque identifier — avoid
FolderPath values whose last segment itself shouldn't be visible to
users of the page. If the resolved folder is itself a filesystem root
(a bare drive like C:\, or a UNC share root like \\Server\Share, which
has no last segment to extract), the label falls back to the full
normalized path instead — e.g. FolderPath("\\\\CorpFileServer01\\Finance")
renders the label \\CorpFileServer01\Finance verbatim, so avoid pointing
FolderPath/sharedRootPath at a share root whose server or share name
shouldn't be visible to users of the page. Rendering the same view repeatedly reuses the same
identifier (no registry growth); avoid parameterizing FolderPath (or
any other configured value) per-request or per-user, since each
distinct combination becomes a permanent in-memory entry for the app's
lifetime — there is no eviction policy in v1.
The identifier is deterministic, not a capability token or access-control
mechanism: it's derived from the configured path, permissions, allowed
extensions, and max upload size, so it can be recomputed by anyone who
knows (or guesses) that configuration. FileManagerController performs no
authentication or authorization of its own — every registered root is
reachable by any caller who can reach /filemanager/*. If a page renders
Html.FileManager() over sensitive content, restrict that page (and the
FileManagerController routes) using the host app's own auth, the same
way you'd protect any other endpoint.
Limitations
- Widgets must all be present in the initial page render.
Html.FileManager()calls added to the page after it has finished loading (e.g. injected via AJAX) will not initialize — widget discovery happens once, when the page first loads. IRootFolderProvidermust be registered as a singleton. Dynamic roots created byHtml.FileManager()are stored in whateverIRootFolderProviderinstance the current request resolves. If it's registered with a scoped or transient lifetime, each render (and each API call) gets its own throwaway registry and the widget will 404 on every request after the first. Register it withAddSingleton, as shown above.
License
MIT — see LICENSE.
Contact
- Email: lylay.it.kh855@gmail.com
- Phone: +855 10 657 986
- LinkedIn: linkedin.com/in/lay-ly-1292311b3
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- MsgReader (>= 6.1.0)
- SkiaSharp (>= 4.150.1)
- SkiaSharp.NativeAssets.Linux (>= 4.150.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.1 | 99 | 8/30/2026 |
| 1.0.0 | 98 | 8/30/2026 |
| 1.0.0-beta.2 | 63 | 8/26/2026 |
| 1.0.0-beta.1 | 85 | 8/25/2026 |