[HttpPost("upload")]
public async Task<IActionResult> Upload(IFormFile file) {
if (file.Length == 0) return BadRequest("Empty file");
using var stream = file.OpenReadStream();
await _storageService.SaveAsync(file.FileName, stream);
return Ok(new { file.FileName, file.Length });
}
Topics
36
API Documentation with Swagger/OpenAPI
API Versioning
Authentication
Authorization
Background Tasks & Hosted Services
Blazor Integration with ASP.NET Core
Caching
Configuration & Options Pattern
Content Negotiation & Output Formatters
Controllers vs Minimal APIs
CORS (Cross-Origin Resource Sharing)
Dependency Injection
Endpoint Metadata, Route Constraints & Templates
Error Handling
File Uploads & Streaming Large Files
Filters
gRPC Services
Health Checks
Hosting Models: Kestrel, IIS & Reverse Proxies
HTTPS, Certificates & Transport Security
Localization & Globalization
Logging
Model Binding & Validation
MVC Views, Razor Syntax & Tag Helpers
Output Caching & Response Caching
Rate Limiting
Razor Pages
Request Pipeline & Middleware
Response Compression & Caching Headers
Routing
Security Headers, Antiforgery & CSRF Protection
Sessions, Cookies & TempData
SignalR & Real-Time Communication
Static Files, wwwroot & Content Delivery
Testing ASP.NET Core Applications
WebSockets
File Uploads & Streaming Large Files
15 questions found
An action parameter of type IFormFile automatically binds an uploaded file from a multipart/form-data request, giving access to its stream (OpenReadStream()), file name, content type, and length -- ASP.NET Core handles the multipart parsing automatically, letting you focus on what to do with the file's content.
Real-world example
A document management API accepts PDF uploads via a simple IFormFile parameter, automatically getting the file's name, size, and content stream without manually parsing the multipart request body.
Model Binding & Validation;Hosting Models: Kestrel
IIS & Reverse Proxies
Why is buffering an entire large file upload in memory (via IFormFile) problematic, and how does streaming avoid this issue?
IntermediateIFormFile by default buffers the entire uploaded file (potentially to memory or a temp disk file depending on size) before your action code even runs, which for very large files (video, large datasets) can cause significant memory pressure or exhaust server resources under concurrent uploads -- streaming instead processes the file's bytes incrementally as they arrive over the network, without ever holding the complete file in memory at once, using Request.Body directly with a multipart reader.
// Buffered approach: entire file loaded before action code runs (problematic for very large files)
[HttpPost]
public async Task<IActionResult> Upload(IFormFile file) { ... }
// Streaming approach: process incrementally without full buffering
[HttpPost]
[DisableFormValueModelBinding]
public async Task<IActionResult> UploadStreamed() {
var reader = new MultipartReader(boundary, Request.Body);
var section = await reader.ReadNextSectionAsync();
// process section.Body as a stream, writing incrementally to storage
}
Real-world example
A video upload service switches from IFormFile-based buffered uploads to true streaming, preventing memory spikes that previously caused occasional out-of-memory crashes when multiple large video uploads happened concurrently.
Diagnostics & Performance;Memory Management & Garbage Collection
How would you implement true streaming file upload using MultipartReader to avoid ASP.NET Core's default buffering behavior entirely?
AdvancedDisable the default form value model binding (via [DisableFormValueModelBinding] and not using an IFormFile parameter at all), then manually read the multipart request using MultipartReader against Request.Body, processing each section's stream incrementally (writing directly to a destination stream like a file or blob storage) without ever materializing the complete file content in memory or a temp file simultaneously.
[HttpPost("stream-upload")]
[DisableFormValueModelBinding]
public async Task<IActionResult> StreamUpload() {
var boundary = MultipartRequestHelper.GetBoundary(MediaTypeHeaderValue.Parse(Request.ContentType));
var reader = new MultipartReader(boundary, Request.Body);
var section = await reader.ReadNextSectionAsync();
while (section != null) {
var fileSection = section.AsFileSection();
if (fileSection != null) {
using var targetStream = File.Create(Path.Combine(_uploadPath, fileSection.FileName));
await fileSection.FileStream.CopyToAsync(targetStream);
}
section = await reader.ReadNextSectionAsync();
}
return Ok();
}
Real-world example
A large media platform accepting multi-gigabyte video uploads implements true MultipartReader-based streaming, keeping memory usage constant regardless of file size, versus the IFormFile approach which would have buffered gigabytes into memory or temp disk per concurrent upload.
Diagnostics & Performance;Hosting Models: Kestrel
IIS & Reverse Proxies
How do you configure and enforce a maximum request/file upload size in ASP.NET Core, and what's the default limit?
IntermediateKestrel's default MaxRequestBodySize is 30MB; configure it via KestrelServerOptions.Limits.MaxRequestBodySize globally, or override per-endpoint via [RequestSizeLimit(bytes)] on a controller action, or disable the limit entirely with [DisableRequestSizeLimit] for endpoints specifically designed to accept very large uploads (used carefully, combined with other safeguards against abuse).
builder.WebHost.ConfigureKestrel(options => {
options.Limits.MaxRequestBodySize = 100_000_000; // 100MB globally
});
[RequestSizeLimit(50_000_000)] // 50MB override for this specific action
[HttpPost("upload")]
public async Task<IActionResult> Upload(IFormFile file) { ... }
Real-world example
A video-sharing platform sets a global 500MB Kestrel limit but overrides a specific low-risk thumbnail-upload endpoint down to a much stricter 5MB via [RequestSizeLimit], preventing that particular endpoint from being abused for oversized uploads.
Hosting Models: Kestrel
IIS & Reverse Proxies;Rate Limiting
How would you implement resumable/chunked file uploads to support very large files over unreliable network connections?
AdvancedA chunked upload protocol splits a large file into smaller pieces uploaded via separate requests (each with a chunk index and total chunk count), with the server accumulating chunks (tracked via a temporary storage location keyed by an upload session ID) and assembling the complete file only once all chunks have arrived -- allowing an interrupted upload to resume from the last successfully received chunk rather than restarting from scratch, essential for large files over unreliable connections.
[HttpPost("upload-chunk")]
public async Task<IActionResult> UploadChunk(string uploadId, int chunkIndex, int totalChunks, IFormFile chunk) {
var chunkPath = Path.Combine(_tempPath, uploadId, $"chunk-{chunkIndex}");
using var stream = File.Create(chunkPath);
await chunk.CopyToAsync(stream);
if (await AllChunksReceivedAsync(uploadId, totalChunks)) {
await AssembleChunksAsync(uploadId, totalChunks);
}
return Ok(new { chunkIndex, received = true });
}
Real-world example
A cloud backup application uploading multi-gigabyte files over an unreliable mobile connection implements chunked, resumable uploads, letting a dropped connection resume from the last successfully uploaded chunk instead of restarting a multi-hour upload from zero.
Background Tasks & Hosted Services;Diagnostics & Performance
How do you stream a large file back to the client as a download response without loading the entire file into memory first?
IntermediateReturning a FileStreamResult (via File(stream, contentType) or Results.Stream() in minimal APIs) with an open stream lets ASP.NET Core copy bytes from the stream directly to the response body incrementally as they're read, rather than requiring the entire file's bytes to be loaded into a byte array in memory before the response can begin sending.
[HttpGet("download/{id}")]
public async Task<IActionResult> Download(int id) {
var stream = await _storageService.OpenReadStreamAsync(id);
return File(stream, "application/octet-stream", "report.pdf");
}
// Minimal API equivalent
app.MapGet("/download/{id}", async (int id, IStorageService storage) => {
var stream = await storage.OpenReadStreamAsync(id);
return Results.Stream(stream, "application/octet-stream", "report.pdf");
});
Real-world example
A reporting API serving multi-hundred-megabyte generated PDF reports streams them directly from blob storage to the client response, keeping server memory usage constant regardless of file size instead of loading each report fully into a byte array first.
Diagnostics & Performance;Content Negotiation & Output Formatters
How do you support HTTP range requests (partial content downloads) for large file streaming, enabling video seeking or resumable downloads?
AdvancedASP.NET Core's File() result methods automatically support range requests (Range header) when the underlying stream supports seeking, returning 206 Partial Content responses for byte-range requests -- this is what enables video players to seek to arbitrary positions without downloading the entire file, and download managers to resume interrupted downloads from a specific byte offset rather than the beginning.
[HttpGet("video/{id}")]
public async Task<IActionResult> StreamVideo(int id) {
var stream = await _storageService.OpenSeekableStreamAsync(id);
return File(stream, "video/mp4", enableRangeProcessing: true);
}
// Client requesting: Range: bytes=1000000-2000000
// Response: 206 Partial Content with just that byte range
Real-world example
A video streaming platform enables range processing on its video download endpoint, letting users seek to any point in a video (their player requests just that byte range) without downloading the entire file up to that point first.
Diagnostics & Performance;Content Negotiation & Output Formatters
How do you validate an uploaded file's actual content type (not just its client-supplied extension or Content-Type header) to prevent malicious file uploads?
IntermediateSince a client can freely lie about a file's Content-Type header or rename a file's extension, robust validation inspects the file's actual binary signature (magic bytes/file header) rather than trusting client-supplied metadata -- libraries like FileSignatures or manual magic-byte checking verify a file genuinely is what it claims to be before accepting it, an important defense against uploading disguised executable or script content.
public bool IsValidImageFile(Stream stream) {
var buffer = new byte[8];
stream.Read(buffer, 0, 8);
stream.Position = 0; // reset for later reading
// PNG magic bytes: 89 50 4E 47 0D 0A 1A 0A
return buffer[0] == 0x89 && buffer[1] == 0x50 && buffer[2] == 0x4E && buffer[3] == 0x47;
}
Real-world example
A user profile picture upload feature rejects a malicious file disguised with a .jpg extension and image/jpeg Content-Type header but actually containing executable script content, caught by inspecting the file's genuine magic bytes rather than trusting client-supplied metadata.
Security Headers
Antiforgery & CSRF Protection;Error Handling
How would you implement direct-to-cloud-storage uploads (bypassing your API server entirely) using pre-signed URLs, and why is this approach preferred for very large files?
AdvancedRather than routing large file bytes through your API server (consuming its bandwidth, memory, and compute resources), your API generates a time-limited, pre-signed upload URL (via the cloud storage provider's SDK, like Azure Blob Storage's SAS tokens or AWS S3's presigned URLs) that the client uploads directly to, with your API only handling the lightweight metadata coordination -- dramatically reducing server load and improving upload performance for large files.
[HttpPost("request-upload-url")]
public IActionResult RequestUploadUrl(string fileName) {
var blobClient = _containerClient.GetBlobClient(fileName);
var sasUri = blobClient.GenerateSasUri(BlobSasPermissions.Write, DateTimeOffset.UtcNow.AddMinutes(15));
return Ok(new { uploadUrl = sasUri.ToString() });
}
// Client then uploads the actual file bytes DIRECTLY to sasUri, bypassing the API server entirely
Real-world example
A video platform handling multi-gigabyte uploads generates a short-lived pre-signed Azure Blob SAS URL for each upload request, letting clients upload directly to blob storage and completely avoiding routing massive file bytes through the API server's own bandwidth and compute.
Diagnostics & Performance;Security Headers
Antiforgery & CSRF Protection
An action parameter of type IFormFileCollection or List<IFormFile> automatically binds all files present in a multipart request with matching form field names, letting you iterate and process each uploaded file, useful for batch upload scenarios like a photo gallery accepting several images at once.
[HttpPost("upload-multiple")]
public async Task<IActionResult> UploadMultiple(List<IFormFile> files) {
var results = new List<string>();
foreach (var file in files) {
if (file.Length > 0) {
await _storageService.SaveAsync(file.FileName, file.OpenReadStream());
results.Add(file.FileName);
}
}
return Ok(new { uploadedFiles = results });
}
Real-world example
A photo gallery feature lets users select and upload up to 10 images in a single form submission, bound automatically as a List<IFormFile> and processed in a single loop rather than requiring 10 separate upload requests.
Model Binding & Validation;Rate Limiting
Showing 1–10 of 15