ASP.NET Core cookbook · Intermediate
Accept file uploads with size and type limits
Uploads can fill the disk, overwrite files or carry the wrong content.
Approach
Accept IFormFile, enforce request size limits, check the extension and the file signature, save under a
generated name outside the web root (or in blob storage), and return the new resource's ID.
Solution
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/api/documents", async (IFormFile file, CancellationToken ct) =>
{
if (file.Length is 0 or > 10 * 1024 * 1024)
return Results.Problem("File must be between 1 byte and 10 MB.", statusCode: 400);
if (!string.Equals(Path.GetExtension(file.FileName), ".pdf", StringComparison.OrdinalIgnoreCase))
return Results.Problem("Only PDF files are accepted.", statusCode: 400);
await using var stream = file.OpenReadStream();
var header = new byte[5];
await stream.ReadExactlyAsync(header, ct);
if (!header.AsSpan().SequenceEqual("%PDF-"u8))
return Results.Problem("The file isn't a valid PDF.", statusCode: 400);
stream.Position = 0;
var id = Guid.NewGuid();
var path = Path.Combine("/var/app-data/documents", $"{id:N}.pdf"); // outside wwwroot
await using var target = File.Create(path);
await stream.CopyToAsync(target, ct);
return Results.Created($"/api/documents/{id}", new { id });
})
.DisableAntiforgery() // API called with bearer tokens, not cookies
.WithMetadata(new Microsoft.AspNetCore.Mvc.RequestSizeLimitAttribute(11 * 1024 * 1024));
app.Run();
Pitfalls
- Using
file.FileNamein the storage path (path traversal, overwrites). - Buffering huge uploads in memory; stream them or upload directly to blob storage with SAS URLs.
- Disabling antiforgery for cookie-authenticated form posts.
Variations
- For large files, stream multipart sections with
MultipartReaderinstead of model binding. - Scan files with an antivirus service before making them available.
Further reading: Upload files
Reference: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads
More ASP.NET Core recipes
- Add OpenTelemetry traces and metrics
- Apply EF Core migrations safely during deployment
- Bind and validate options at startup
- Cache responses with output caching and evict by tag
- Call external APIs with typed HttpClients and resilience
- Configure CORS for a browser front end
- Expose liveness and readiness health checks
- Generate OpenAPI documents with Microsoft.AspNetCore.OpenApi