ASP.NET Core MVC Mastery

File Upload/Download

1 Views Updated 5/4/2026

File Uploads & Downloads in ASP.NET Core MVC

Handling files is notoriously dangerous. A poorly implemented file upload feature can easily lead to Out of Memory exceptions, Path Traversal hacks, or malicious malware execution. Mastering ASP.NET Core's IFormFile, streaming techniques, and strict security validation is essential for enterprise web applications.

1. Buffering vs. Streaming

ASP.NET Core provides two entirely different paradigms for handling incoming files depending on the file's size.

Buffering (IFormFile)

The entire file is read into the web server's RAM (or a temporary disk buffer) before the controller method even begins executing. Use Case: Small files like user avatars or PDFs (e.g., < 20MB).

Streaming (MultipartReader)

The file is processed in small chunks directly from the network stream and piped directly to storage (like AWS S3) without loading the whole file into RAM. Use Case: Large video files, massive datasets (e.g., > 100MB).

2. REAL-WORD PRODUCTION UPLOADS (Buffering)

Step 1: The Razor View

Your HTML form must have the enctype="multipart/form-data" attribute, or the file will not be sent.

<form asp-action="UploadAvatar" method="post" enctype="multipart/form-data">
    <div class="mb-3">
        <label for="file">Select Profile Picture (JPEG/PNG only)</label>
        <!-- Accept attribute provides client-side hints, but NEVER trust it -->
        <input type="file" name="file" class="form-control" accept=".jpg,.jpeg,.png" />
    </div>
    <button type="submit" class="btn btn-primary">Upload</button>
</form>

Step 2: The Secure Controller Action

public class ProfileController : Controller
{
    private readonly IWebHostEnvironment _env;

    // Inject IWebHostEnvironment to safely resolve physical server paths
    public ProfileController(IWebHostEnvironment env) => _env = env;

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> UploadAvatar(IFormFile file)
    {
        // 1. Basic Validation
        if (file == null || file.Length == 0)
            return BadRequest("File is empty.");

        if (file.Length > 5 * 1024 * 1024) // 5MB limit
            return BadRequest("File exceeds 5MB limit.");

        // 2. Security: Extension Validation
        var extension = Path.GetExtension(file.FileName).ToLowerInvariant();
        var allowedExtensions = new[] { ".jpg", ".jpeg", ".png" };
        if (!allowedExtensions.Contains(extension))
            return BadRequest("Invalid image format.");

        // 3. Security: Generate a RANDOMLY GUID filename.
        // NEVER use the client's file.FileName. It can contain "../../../malicious.exe" (Path Traversal)
        var trustedFileName = Guid.NewGuid().ToString() + extension;

        // 4. Resolve the safe directory (e.g., wwwroot/uploads/avatars)
        var uploadPath = Path.Combine(_env.WebRootPath, "uploads", "avatars");
        Directory.CreateDirectory(uploadPath); // Ensure it exists

        var fullPhysicalPath = Path.Combine(uploadPath, trustedFileName);

        // 5. Asynchronously write the buffered IFormFile to the actual physical disk
        using (var stream = new FileStream(fullPhysicalPath, FileMode.Create))
        {
            await file.CopyToAsync(stream);
        }

        // 6. Save the relative path to your database
        var dbPath = $"/uploads/avatars/{trustedFileName}";
        // await _db.SaveAvatarPath(userId, dbPath);

        return RedirectToAction("Index");
    }
}

3. Handling File Downloads

File downloads use the FileResult base class. There are three primary physical implementations depending on your source data.

public class DownloadController : Controller
{
    // 1. PhysicalFile (): When the file exists natively on the web server's hard drive
    public IActionResult DownloadPdf()
    {
        var filepath = Path.Combine(_env.ContentRootPath, "secure_docs", "report.pdf");
        return PhysicalFile(filepath, "application/pdf", "FinancialReport2026.pdf");
    }

    // 2. FileStreamResult via File(): When retrieving from Cloud Storage or a Database Stream
    public async Task<IActionResult> DownloadFromAzureBlobObject(string fileId)
    {
        Stream blobStream = await _azureBlobService.DownloadBlobAsync(fileId);
        // The framework automatically disposes the stream when the download finishes
        return File(blobStream, "application/octet-stream", "download.zip");
    }

    // 3. FileContentResult via File(): When generating files in RAM (e.g., creating CSVs on the fly)
    public IActionResult DownloadCsv()
    {
        byte[] csvData = Encoding.UTF8.GetBytes("Id,Name
1,Sandeep
2,John");
        return File(csvData, "text/csv", "users.csv");
    }
}

4. Security Checklist & Best Practices

  • Whitelist Extensions, but verify signatures: Hackers can rename virus.exe to virus.jpg. Advanced enterprise apps read the first few bytes (the "magic number") of the file stream to verify the file is genuinely a JPEG before saving it.
  • Isolate Uploads: Never save user uploads in the same folder as executable application code. Ideally, save them directly to Azure Blob Storage or AWS S3, bypassing the web server's disk entirely.
  • Do not trust Anti-Virus entirely: Files uploaded should be sent to an isolated queuing system for malware scanning before being marked "available" for download by other users.

5. Interview Mastery

Q: "Why should you never use `IFormFile` to accept a 2GB video upload in ASP.NET Core?"

Architect Answer: "`IFormFile` works by buffering the entire uploaded file. While ASP.NET Core shifts buffering to disk if the file exceeds a certain limit (default 64KB), the initial ingestion still ties up server resources, and accessing it requires moving massive amounts of IO synchronously. A malicious user could launch a DoS attack by uploading multiple 2GB videos simultaneously, exhausting server disk space or memory. For files over ~50MB, you must abandon Model Binding (`IFormFile`) entirely and use the `MultipartReader` class, reading the raw HTTP Request Body stream directly and piping the bytes outward to cloud storage as they arrive, maintaining a minimal memory footprint."

ASP.NET Core MVC Mastery
1. Core Framework
Introduction to ASP.NET Core MVC
MODULE 1: INTRODUCTION & ENVIRONMENT SETUP
Microsoft Web Stack Overview Evolution of ASP.NET Environment Setup
2. View Engine
Layouts & Partial Views in Razor
MODULE 2: .NET CORE FUNDAMENTALS
Core Concepts Project Structure Startup Flow Middleware Pipeline
MODULE 3: ASP.NET CORE BASICS
Creating Project CLI Commands wwwroot & Static Files
MODULE 4: MVC FUNDAMENTALS
MVC Architecture Dependency Injection (DI) Service Lifetimes
MODULE 5: DATA PASSING TECHNIQUES
ViewData vs ViewBag TempData ViewModel Pattern
MODULE 6: ROUTING
Conventional vs Attribute Routing Custom Constraints
MODULE 7: VIEWS & UI
Razor View Engine Layouts & Sections View Components
MODULE 8: ACTION RESULTS
ViewResult JsonResult RedirectResult
MODULE 9: HTML HELPERS
Form Helpers Custom HTML Helpers
MODULE 10: TAG HELPERS
Built-in Tag Helpers Custom Tag Helpers
MODULE 11: MODEL BINDING
FromQuery vs FromRoute Complex Binding
MODULE 12: VALIDATION
Data Annotations Remote Validation Fluent Validation
MODULE 13: STATE MANAGEMENT
Cookies & Sessions TempData
MODULE 14: FILTERS & SECURITY
Action Filters Authorize Filters Anti-forgery
MODULE 15: ENTITY FRAMEWORK CORE (DEEP DIVE)
DbContext Migrations LINQ Relationships
MODULE 16: DESIGN PATTERNS
Repository Pattern Unit of Work Clean Architecture
MODULE 17: FILE HANDLING
File Upload/Download PDF/Excel Generation
MODULE 18: ADVANCED ASP.NET CORE
Request Lifecycle Bundling & Minification Deployment
MODULE 19: PERFORMANCE & BEST PRACTICES
Caching Strategies Async Programming Secure Coding
MODULE 20: RAZOR PAGES (BONUS)
Razor Pages vs MVC
MODULE 21: REAL-WORLD PROJECTS (🔥 MUST DO)
E-Commerce Web Application Employee Management System