FileHub.Core 1.0.1

dotnet add package FileHub.Core --version 1.0.1
                    
NuGet\Install-Package FileHub.Core -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FileHub.Core" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FileHub.Core" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="FileHub.Core" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FileHub.Core --version 1.0.1
                    
#r "nuget: FileHub.Core, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FileHub.Core@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FileHub.Core&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=FileHub.Core&version=1.0.1
                    
Install as a Cake Tool

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, ...); PhysicalFileSystemProvider is the built-in local-disk implementation.
  • Security/PathValidator (traversal / absolute-path / symlink-escape protection) and FilenameSanitizer. Every provider operation should resolve through PathValidator before 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 via IRootFolderProvider.GetOrRegister — used by Html.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 to soffice --headless --convert-to pdf, disk-cached, per-cache-key locking. Requires LibreOffice installed on the host machine (configure its path via LibreOfficeOptions.ExecutablePath).
  • Preview/PreviewFileClassifier: which extensions need office conversion vs. streaming as-is.
  • Extensions/AddFileHubCore(): registers IRootFolderProvider, UploadSessionManager, ThumbnailService, and IOfficeConversionService in one call, using the App_Data/.uploads, App_Data/.cache/thumbnails, and App_Data/.cache/office-pdf paths under the given content root. Its optional sharedRootPath argument is the base directory a relative .FolderPath(...) in Html.FileManager() resolves against (defaults to the content root when omitted). Framework-agnostic (IServiceCollection only) — 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 via AddFileHubMvc().
    • Extensions/Html.FileManager() (returns a fluent builder) and AddFileHubMvc() (registers the controller as an application part on the host's IMvcBuilder).
    • Rendering/FileManagerBuilder (the fluent .FolderPath(...).Permissions(PermissionFlags...)... object; computes a stable identifier from its configuration and registers it via IRootFolderProvider.GetOrRegister so the physical path never reaches the browser) and FileManagerMarkup (builds the widget's HTML directly in C#, no .cshtml involved).
  • 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 under Widget/) since ASP.NET Core's static web asset convention only auto-discovers a top-level wwwroot/, 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.
  • IRootFolderProvider must be registered as a singleton. Dynamic roots created by Html.FileManager() are stored in whatever IRootFolderProvider instance 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 with AddSingleton, as shown above.

License

MIT — see LICENSE.

Contact

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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