KillaCore.Blazor.FileUpload 1.3.1

dotnet add package KillaCore.Blazor.FileUpload --version 1.3.1
                    
NuGet\Install-Package KillaCore.Blazor.FileUpload -Version 1.3.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="KillaCore.Blazor.FileUpload" Version="1.3.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="KillaCore.Blazor.FileUpload" Version="1.3.1" />
                    
Directory.Packages.props
<PackageReference Include="KillaCore.Blazor.FileUpload" />
                    
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 KillaCore.Blazor.FileUpload --version 1.3.1
                    
#r "nuget: KillaCore.Blazor.FileUpload, 1.3.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 KillaCore.Blazor.FileUpload@1.3.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=KillaCore.Blazor.FileUpload&version=1.3.1
                    
Install as a Cake Addin
#tool nuget:?package=KillaCore.Blazor.FileUpload&version=1.3.1
                    
Install as a Cake Tool

KillaCore.Blazor.FileUpload

A high-performance, secure, and modern file upload pipeline designed for Blazor Server, Blazor WebAssembly, and Blazor WebApp (Auto) applications.

This library solves the fundamental issue of Blazor SignalR message size limits by offloading the actual data transfer to a dedicated Javascript worker (XMLHttpRequest) and Web API pipeline. It strictly separates the UI state machine from server-side CPU-bound operations (hashing, duplicate verification, and disk saving), ensuring your Blazor UI remains highly responsive regardless of file size.

🚀 Key Features

  • Dual-Package Architecture: Seamlessly supports .NET 10 Blazor WebApp Auto rendering modes by strictly separating Client UI logic from Server API logic.
  • SignalR Bypass: Transfers files directly to an API controller, completely preventing Blazor Server circuit disconnects and memory spikes.
  • Multi-Tier Duplicate Detection: Saves bandwidth and server resources by intelligently skipping identical files within the same batch using instant client-side heuristics and server-side SHA-256 cryptographic hashing.
  • End-to-End Security:
    • Anti-Replay Tokens: Uses short-lived, one-time-use secure tokens to prevent unauthorized API access.
    • Encrypted Policies: Uses IDataProtection to encrypt validation rules (like Allowed MIME types) so they cannot be tampered with on the client.
    • Magic Number Inspection: Validates actual file content headers on the server, ensuring users cannot bypass security by renaming file extensions.
  • Extensible Server Hooks: A clean IFileUploadServerHooks interface allows you to execute custom logic (saving to DB, AWS S3, Azure Blob, or local disk) without altering the core pipeline.
  • Resiliency & Cancellation: Full support for CancellationToken propagation. Cancelling a batch in the UI immediately halts network requests and server-side saving operations.
  • Orphan Cleanup Janitor: Includes a lightweight, zero-impact BackgroundService that automatically sweeps the temporary storage folder to prevent disk exhaustion in the event of hard server crashes or IIS App Pool recycles.
  • Extensible Server Hooks: A clean IFileUploadServerHooks interface allows you to execute custom logic (saving to DB, AWS S3, Azure Blob, or local disk) without altering the core pipeline.
    • Dynamic Hook Routing: Leverages modern .NET Keyed Dependency Injection, allowing you to register multiple unique upload behaviors (e.g., "ProfilePictures" vs "Invoices") in the same app and route them effortlessly from the UI.

📦 Installation & Setup

Because this solution utilizes a secure Client-to-Server architecture, you must install the appropriate packages based on your Blazor hosting model.

1. Install the NuGet Packages

For the Server Project (API, Validation, Disk I/O):

dotnet add package KillaCore.Blazor.FileUpload

For the Client Project (UI Components, WASM, JS Interop):

dotnet add package KillaCore.Blazor.FileUpload.Client

2. Register Services (Program.cs)

Your setup depends on your Blazor hosting model.

Option A: Blazor WebApp (Auto Mode) OR Blazor WebAssembly (Hosted)
  • Client Project Program.cs: Register the client services so the UI can communicate with your API.
    builder.Services.AddKillaCoreFileUploadClient();
    
  • Server Project Program.cs: Register the server-side validation, caching, and API services. Also register the client services here if you are utilizing Server Prerendering.
    // 1. Required for the UploadsController API
    builder.Services.AddControllers();
    
    // 2. Register KillaCore Packages
    builder.Services.AddKillaCoreFileUploadServer(builder.Configuration);;
    builder.Services.AddKillaCoreFileUploadClient(); // If prerendering Client components
    
    // 3. Register your custom server hooks using Keyed DI!
    builder.Services.AddScoped<IFileUploadServerHooks, ApplicationUploadHooks>();
    
    // The string key tells the server which logic to execute based on the UI's context.
    // Example: You can register as many different hooks as your app needs!
    // builder.Services.AddKeyedScoped<IFileUploadServerHooks, ApplicationUploadHooks>("Default");
    // builder.Services.AddKeyedScoped<IFileUploadServerHooks, ProfilePictureHooks>("ProfileImages");
    // builder.Services.AddKeyedScoped<IFileUploadServerHooks, InvoiceHooks>("Invoices");
    
Option B: Blazor Server (Interactive Server Only)

Since everything runs on one machine, register everything in your single Program.cs:

builder.Services.AddControllers();

builder.Services.AddKillaCoreFileUploadServer(builder.Configuration);;
builder.Services.AddKillaCoreFileUploadClient();

builder.Services.AddScoped<IFileUploadServerHooks, ApplicationUploadHooks>();

3. Map the Controllers (Server Project)

Ensure your backend is set up to route requests to the package's internal UploadsController.

app.MapControllers(); // Ensure this is before app.Run()

4. Configure appsettings.json (Server Project)

You must define a secure key for generating Anti-Replay tokens, and optionally specify where temporary files should be stored during the upload process.

{
  "KillaCoreFileUpload": {
    "SecretKey": "Your-Super-Secure-Secret-Key-Min-16-Chars",
    "TempFolder": "KillaCoreUploads" 
  }
}

Note: If TempFolder is just a name, it will be placed in the OS temp directory. You can also provide an absolute path like D:\TempUploads.


4. Implement Server Hooks (Your Custom Logic)

The package handles the networking, validation, and temporary file creation automatically. However, you must tell the application what to do with the files once they safely reach your server.

In your Server Project, create a class that implements IFileUploadServerHooks:

using KillaCore.Blazor.FileUpload.Services;
using KillaCore.Blazor.FileUpload.Client.Models;

public class ApplicationUploadHooks : IFileUploadServerHooks
{
    // 1. Check if the file already exists (Optional)
    public Task<bool> CheckRemoteDuplicateAsync(string detectedHash, CancellationToken ct)
    {
        // Example: bool exists = _dbContext.Files.Any(f => f.Hash == detectedHash);
        return Task.FromResult(false); 
    }

    // 2. Save the file to your permanent storage
    public async Task SaveFileAsync(FileTransferData data, Stream fileStream, CancellationToken ct)
    {
        string safeName = $"{Guid.NewGuid():N}{Path.GetExtension(data.FileName)}";
        string path = Path.Combine("C:\\SecureUploads", safeName);

        await using var fs = new FileStream(path, FileMode.CreateNew, FileAccess.Write);
        
        // Pass the CancellationToken to support immediate UI cancellation!
        await fileStream.CopyToAsync(fs, ct);

        // Map your database ID back to the model so the Blazor UI Client knows about it
        data.FinalResourceId = safeName; 
    }

    // 3. Fire logic when the whole batch finishes (Emails, Database updates, etc.)
    public Task OnBatchCompletedAsync(string batchId, IReadOnlyList<FileTransferData> files)
    {
        int successCount = files.Count(f => f.Status == TransferStatus.Completed);
        Console.WriteLine($"Batch {batchId} finished. {successCount} files saved successfully.");
        return Task.CompletedTask;
    }
}

💻 Usage Example (The UI Component)

You can now use the FileUploadProcessor in your Client Project (.razor files). The component operates in a "headless" state-machine style, giving you full control over how you render the progress bars.

@page "/upload"
@using KillaCore.Blazor.FileUpload.Client.Components
@using KillaCore.Blazor.FileUpload.Client.Models

<h3>Secure File Processor</h3>

<div class="mb-3">
    <InputFile id="myFileInput" OnChange="HandleInputOnChange" multiple class="form-control" />
</div>

@if (_processor?.Transfers.Any() == true)
{
    <ul class="list-group mb-3">
        @foreach (var file in _processor.Transfers)
        {
            <li class="list-group-item">
                <div class="d-flex justify-content-between">
                    <strong>@file.FileName</strong>
                    <span class="badge bg-secondary">@file.Status.ToString()</span>
                </div>
                
                <div class="progress mt-2" style="height: 20px;">
                    <div class="progress-bar @(file.IsFinished ? "bg-success" : "progress-bar-animated progress-bar-striped")" 
                         style="width: @(file.LifecyclePercent)%;">
                        @file.LifecyclePercent.ToString("0")%
                    </div>
                </div>
                <small class="text-muted">@file.StatusMessage</small>
            </li>
        }
    </ul>
    
    @if (!_processor.Transfers.All(x => x.IsFinished))
    {
        <button class="btn btn-danger" @onclick="() => _processor.CancelAllAsync()">Cancel Batch</button>
    }
    else
    {
        <button class="btn btn-secondary" @onclick="() => _processor.Clear()">Clear List</button>
    }
}

<FileUploadProcessor @ref="_processor"
                     InputSelector="#myFileInput"
                     Options="_options"
                     UserId="@CurrentUserId"
                     OnEvent="HandleUploadEvent" />

@code {
    private FileUploadProcessor _processor = default!;
    private string CurrentUserId = "User-123"; // Retrieve from auth state

    // Configure specific limits and features
    private readonly FileProcessingOptions _options = new()
    {
        MaxFiles = 5,
        MaxSizeFileBytes = 1024 * 1024 * 500, // 500 MB limit
        MaxConcurrentUploads = 2,             // Throttle network connections
        AllowedMimeTypes = ["image/jpeg", "image/png", "application/pdf"],
        EnabledFeatures = [
            FileUploadFeature.VerifyLocalDuplicates, 
            FileUploadFeature.VerifyRemoteDuplicates, 
            FileUploadFeature.SaveToServer
        ],
        UploadContext = "Default" // Must match the string used in AddKeyedScoped!
    };

    private async Task HandleInputOnChange(InputFileChangeEventArgs e)
    {
        // Pass the files to the processor to begin the pipeline
        var files = e.GetMultipleFiles(_options.MaxFiles).ToList();
        if (_processor != null)
        {
            await _processor.ProcessInputFiles(files);
        }
    }

    // Callback: Update UI when the Javascript worker reports progress
    private void HandleUploadEvent(FileNotificationEvent notification)
    {
        StateHasChanged();
    }
}

⚙️ Configuration (FileProcessingOptions)

Property Default Description
UploadContext "Default" The string key used by the API to resolve the specific IFileUploadServerHooks implementation via Keyed DI.
MaxFiles 10 Maximum number of files allowed in a single batch selection.
MaxSizeFileBytes 50 MB Hard size limit per file. Validated instantly on the client.
AllowedMimeTypes * (Any) List of allowed MIME types. Sent securely as an encrypted policy.
MaxConcurrentUploads 5 Throttle limit for how many parallel XHR network requests are executed.
EnabledFeatures All Toggle VerifyLocalDuplicates, VerifyRemoteDuplicates, or SaveToServer flags.

⚠️ Important Production Considerations

IIS & Web Server Request Limits

By default, ASP.NET Kestrel and IIS block HTTP POST requests larger than 30MB. Even though this component supports 500MB+ configurations, your web server will reject the request with a 413 Payload Too Large error before it ever reaches the UploadsController.

  • IIS (web.config): You must increase the maxAllowedContentLength.
<security>
  <requestFiltering>
    <requestLimits maxAllowedContentLength="524288000" />
  </requestFiltering>
</security>
  • Kestrel (Program.cs): You must configure MaxRequestBodySize on the Kestrel server options.

Web Farms & Docker Swarm

This package relies on IDataProtectionProvider to encrypt upload policies. If you deploy your API across multiple servers or load balancers, you must configure a centralized Data Protection key ring (e.g., storing keys in Redis, Azure Blob Storage, or a shared network folder). If Server A encrypts the policy and the Load Balancer routes the file transfer to Server B, the upload will fail with a CryptographicException if Server B does not share the same decryption key.


📄 License

MIT License.

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.3.1 89 5/5/2026
1.3.0 89 5/5/2026
1.2.0 89 5/5/2026
1.1.3 93 5/4/2026
1.1.2 85 5/4/2026
1.1.1 108 4/7/2026
1.1.0 112 4/1/2026
1.0.12 163 1/8/2026
1.0.11 140 1/5/2026
1.0.10 207 11/25/2025
1.0.9 200 11/25/2025
1.0.8 197 11/24/2025
1.0.7 196 11/24/2025
1.0.6 194 11/24/2025
1.0.5 200 11/24/2025
1.0.4 198 11/24/2025
1.0.3 199 11/24/2025
1.0.2 192 11/24/2025
1.0.1 197 11/24/2025
1.0.0 202 11/23/2025
Loading failed