# How to Build Minimal APIs with File Upload Support in ASP.NET Core

> Learn to build ASP.NET Core minimal APIs with file upload support. Effortlessly stream files to disk using IFormFile in MapPost handlers for efficient web applications.

- Repository: [.NET Platform/skills](https://github.com/dotnet/skills)
- Tags: how-to-guide
- Published: 2026-07-07

---

**ASP.NET Core minimal APIs handle file uploads through the `IFormFile` model binder in `MapPost` handlers, streaming uploaded files to disk while leveraging the same middleware pipeline as traditional MVC applications.**

The `dotnet/skills` repository demonstrates production-ready file handling patterns that translate directly into lightweight minimal API endpoints. By eliminating controllers and views, you create concise HTTP endpoints in a single [`Program.cs`](https://github.com/dotnet/skills/blob/main/Program.cs) file while retaining full access to form-data binding and static file serving. This approach reduces boilerplate while maintaining enterprise-grade file upload capabilities.

## Configuring the Minimal API Host

A minimal API starts with `WebApplication.CreateBuilder` and registers only the services you need. For file uploads, you must configure form options and static file handling.

```csharp
using Microsoft.AspNetCore.Http.Features;

var builder = WebApplication.CreateBuilder(args);

// Optional: increase the default 30 MB multipart limit
builder.Services.Configure<FormOptions>(options =>
{
    options.MultipartBodyLengthLimit = 100L * 1024 * 1024; // 100 MB
});

var app = builder.Build();

// Enable static file serving for downloading uploaded files later
app.UseStaticFiles();
app.Run();

```

This configuration mirrors the service registration pattern found in [`tests/dotnet-upgrade/migrate-nullable-references/fixtures/enable-nrt-in-asp-net-core-web-api-with-ef-core/BookStore/Program.cs`](https://github.com/dotnet/skills/blob/main/tests/dotnet-upgrade/migrate-nullable-references/fixtures/enable-nrt-in-asp-net-core-web-api-with-ef-core/BookStore/Program.cs), which demonstrates how ASP.NET Core Web APIs register infrastructure services.

## Creating the File Upload Endpoint

The `MapPost` method defines an endpoint that accepts `IFormFile` when the request uses `multipart/form-data` encoding. The minimal API pipeline automatically binds the uploaded file to the parameter.

```csharp
app.MapPost("/upload", async (IFormFile file) =>
{
    if (file is null || file.Length == 0)
    {
        return Results.BadRequest("No file uploaded.");
    }

    // Ensure upload directory exists
    var uploadPath = Path.Combine(app.Environment.WebRootPath, "uploads");
    Directory.CreateDirectory(uploadPath);

    // Sanitize filename and ensure uniqueness
    var safeName = Path.GetFileNameWithoutExtension(file.FileName);
    var ext = Path.GetExtension(file.FileName);
    var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
    var finalName = $"{safeName}_{timestamp}{ext}";
    var filePath = Path.Combine(uploadPath, finalName);

    // Stream file to disk efficiently
    await using var stream = File.Create(filePath);
    await file.CopyToAsync(stream);

    var url = $"/uploads/{finalName}";
    return Results.Created(url, new { FileName = finalName, Url = url });
})
    .Accepts<IFormFile>("multipart/form-data")
    .Produces(StatusCodes.Status201Created)
    .Produces(StatusCodes.Status400BadRequest);

```

The `Accepts` metadata tells Swagger/OpenAPI clients that this endpoint expects form-data, while `Produces` documents the possible response codes. This implementation follows the file streaming pattern shown in [`tests/dotnet-test/code-testing-agent/ContosoUniversity/Controllers/CoursesController.cs`](https://github.com/dotnet/skills/blob/main/tests/dotnet-test/code-testing-agent/ContosoUniversity/Controllers/CoursesController.cs), adapted for the minimal API syntax.

## Adapting MVC File Handling Patterns

The repository's [`CoursesController.cs`](https://github.com/dotnet/skills/blob/main/CoursesController.cs) contains a traditional MVC implementation that you can port directly to minimal APIs. The core logic—checking `IFormFile.Length`, combining paths with `Path.Combine`, and streaming via `CopyToAsync`—remains identical.

```csharp
// From Controllers/CoursesController.cs (MVC pattern)
public ActionResult Create(Course course, IFormFile teachingMaterialImage)
{
    if (teachingMaterialImage != null && teachingMaterialImage.Length > 0)
    {
        var fileName = Path.GetFileName(teachingMaterialImage.FileName);
        var path = Path.Combine(_environment.WebRootPath, "uploads", fileName);
        using var stream = new FileStream(path, FileMode.Create);
        teachingMaterialImage.CopyTo(stream);
        course.TeachingMaterialImagePath = $"/uploads/{fileName}";
    }
    return RedirectToAction(nameof(Index));
}

```

To convert this to a minimal API handler, remove the `ActionResult` return type and `RedirectToAction` calls, replacing them with `Results.Created` or `Results.Ok` JSON responses. The file validation and storage logic transfers directly.

## Security and Performance Considerations

Path traversal attacks are mitigated by `Path.GetFileName` and `Path.GetFileNameWithoutExtension`, which strip directory information from the original filename. Always combine these sanitized names with `Path.Combine` rather than string concatenation.

For large files, the `CopyToAsync` method streams the upload directly to disk without buffering the entire file in memory. This prevents out-of-memory exceptions when handling multi-gigabyte uploads, as implemented in the repository's test fixtures under `tests/dotnet-test/code-testing-agent/ContosoUniversity/`.

## Summary

- **Minimal APIs use `IFormFile`** in `MapPost` handlers to receive multipart/form-data uploads without MVC controller overhead.
- **Configure `FormOptions`** via `builder.Services.Configure` to increase the default 30 MB upload limit.
- **Sanitize filenames** using `Path.GetFileName` and generate unique names to prevent overwriting and path traversal attacks.
- **Stream files asynchronously** with `CopyToAsync` to avoid memory pressure on the server.
- **Reference [`CoursesController.cs`](https://github.com/dotnet/skills/blob/main/CoursesController.cs)** in the `dotnet/skills` repository for production-grade file validation logic that adapts to minimal API endpoints.

## Frequently Asked Questions

### How do I set a maximum file size limit for uploads in minimal APIs?

Configure the `FormOptions` service in your application builder. Use `builder.Services.Configure<FormOptions>(options => options.MultipartBodyLengthLimit = sizeInBytes)` to override the default 30 MB limit. You can also apply the `[RequestSizeLimit]` attribute to specific endpoints if you prefer per-route configuration.

### Can I upload multiple files in a single minimal API request?

Yes, change the handler parameter to accept `IFormFileCollection files` or `List<IFormFile> files`. The model binder automatically populates the collection with all files from the multipart request. Iterate through the collection and process each file individually, checking `Length` and `ContentType` as needed.

### Where should I store uploaded files in a minimal API application?

Store files in a subdirectory under `wwwroot` (accessible via `app.Environment.WebRootPath`) when you want to serve them directly through static files middleware. For sensitive files, store them outside the web root and create a separate `MapGet` endpoint that reads and returns the file with proper authorization checks, similar to the security patterns found in the repository's [`Program.cs`](https://github.com/dotnet/skills/blob/main/Program.cs) examples.

### Does minimal API support the same file validation as MVC controllers?

Yes, minimal APIs support the same `IFormFile` interface and validation properties (`Length`, `FileName`, `ContentType`). While minimal APIs don't use model binding attributes like `[Bind]`, you can implement validation logic directly in the handler or use the `FluentValidation` library. The file handling implementation in [`CoursesController.cs`](https://github.com/dotnet/skills/blob/main/CoursesController.cs) demonstrates validation checks that work identically in minimal API handlers.