Works with any backend npm install Zero dependencies
← All Backend Guides

ASP.NET Core

Handle MultipleUpload file uploads with ASP.NET Core minimal APIs using IFormFile. This guide covers both standard multipart uploads and chunked uploads.

Multipart Form Upload

Use ASP.NET Core minimal APIs to receive multipart/form-data uploads via IFormFile. Files are saved to disk with a unique GUID filename.

// Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(policy =>
    {
        policy.AllowAnyOrigin()
              .AllowAnyMethod()
              .WithHeaders("Content-Type", "X-Upload-Id", "X-Chunk-Index",
                           "X-Chunk-Count", "X-File-Name", "X-File-Size");
    });
});

var app = builder.Build();
app.UseCors();

var uploadDir = Path.Combine(Directory.GetCurrentDirectory(), "uploads");
var chunkDir = Path.Combine(uploadDir, "chunks");
Directory.CreateDirectory(uploadDir);

app.MapPost("/api/upload", async (IFormFile file) =>
{
    var fileGuid = Guid.NewGuid().ToString();
    var ext = Path.GetExtension(file.FileName);
    var destPath = Path.Combine(uploadDir, fileGuid + ext);

    using (var stream = new FileStream(destPath, FileMode.Create))
    {
        await file.CopyToAsync(stream);
    }

    return Results.Ok(new
    {
        success = true,
        fileGuid,
        fileName = file.FileName,
        fileSize = file.Length
    });
})
.DisableAntiforgery();

Chunked Upload

Each chunk arrives as a raw body with custom headers. Store chunks in a temporary directory and assemble the final file once all chunks have arrived.

// Add after the /api/upload endpoint
app.MapPost("/api/upload-chunk", async (HttpContext context) =>
{
    var headers = context.Request.Headers;
    var uploadId   = headers["X-Upload-Id"].ToString();
    var chunkIndex = int.Parse(headers["X-Chunk-Index"].ToString());
    var chunkCount = int.Parse(headers["X-Chunk-Count"].ToString());
    var fileName   = headers["X-File-Name"].ToString();
    var fileSize   = long.Parse(headers["X-File-Size"].ToString());

    if (string.IsNullOrEmpty(uploadId) || chunkCount == 0)
    {
        return Results.BadRequest(new { success = false, error = "Missing headers" });
    }

    var sessionDir = Path.Combine(chunkDir, uploadId);
    Directory.CreateDirectory(sessionDir);

    // Save this chunk from raw body
    var chunkPath = Path.Combine(sessionDir, $"chunk_{chunkIndex}");
    using (var fs = new FileStream(chunkPath, FileMode.Create))
    {
        await context.Request.Body.CopyToAsync(fs);
    }

    // Count received chunks
    var received = Directory.GetFiles(sessionDir, "chunk_*").Length;

    if (received == chunkCount)
    {
        // Assemble final file
        var fileGuid = Guid.NewGuid().ToString();
        var ext = Path.GetExtension(fileName);
        var finalPath = Path.Combine(uploadDir, fileGuid + ext);

        using (var output = new FileStream(finalPath, FileMode.Create))
        {
            for (var i = 0; i < chunkCount; i++)
            {
                var cp = Path.Combine(sessionDir, $"chunk_{i}");
                using var chunkStream = File.OpenRead(cp);
                await chunkStream.CopyToAsync(output);
            }
        }

        // Clean up chunks
        Directory.Delete(sessionDir, recursive: true);

        return Results.Ok(new
        {
            success = true,
            fileGuid,
            fileName,
            fileSize
        });
    }

    return Results.Ok(new { success = true, chunksReceived = received });
});

app.Run();

Kestrel Configuration

Increase the maximum request body size in Program.cs or appsettings.json if you need to accept files larger than 30 MB.

// In Program.cs, before builder.Build()
builder.WebHost.ConfigureKestrel(options =>
{
    options.Limits.MaxRequestBodySize = 200 * 1024 * 1024; // 200 MB
});

Expected JSON Response

Both endpoints must return this JSON structure on successful upload completion.

{
  "success": true,
  "fileGuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "fileName": "photo.jpg",
  "fileSize": 204800
}

Client-Side JavaScript

Initialize MultipleUpload on the client, pointing to your ASP.NET Core API endpoints.

<link rel="stylesheet" href="multipleupload.css" />
<div id="uploader"></div>
<script src="multipleupload.js"></script>
<script>
  var uploader = new MultipleUpload("#uploader", {
    uploadUrl: "/api/upload",
    chunkUploadUrl: "/api/upload-chunk",
    chunkSize: 2 * 1024 * 1024, // 2 MB chunks
    onFileUploaded: function (file, response) {
      console.log("Uploaded:", response.fileName, response.fileGuid);
    }
  });
</script>