Skip to footer content
USING IRONPDF

How to Upload and Download PDF Files in ASP.NET Core with C#

Uploading and downloading PDF files in ASP.NET Core requires handling binary data, managing controller actions, and -- optionally -- processing documents server-side before storage or delivery. With IronPDF, you can go beyond simple file storage by applying watermarks, generating PDFs from HTML, and returning processed documents to users, all within your existing MVC pipeline. This guide walks you through each step of building a complete upload-download workflow in C# using .NET 10.

How Do You Install IronPDF in an ASP.NET Core Project?

Before writing any upload or download logic, add IronPDF to your project using the NuGet Package Manager or the .NET CLI. Use Install-Package IronPdf in the Package Manager Console, or run the CLI commands below to scaffold a new MVC project and add all required packages at once.

dotnet new mvc -n PdfManager --framework net10.0
cd PdfManager
dotnet add package IronPdf
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet new mvc -n PdfManager --framework net10.0
cd PdfManager
dotnet add package IronPdf
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Design
SHELL

Once installed, IronPDF provides access to ChromePdfRenderer for generating PDFs from HTML, PdfDocument for loading and manipulating existing files, and a range of editing tools including watermarks, stamps, and digital signatures. You can review the full IronPDF NuGet package page for version history and compatibility notes.

Setting Up the Project

Add a storage path constant to Program.cs and register your ApplicationDbContext with the dependency injection container. Your project structure will include a Controllers/PdfController.cs, a Models/PdfFileModel.cs, and a Data/ApplicationDbContext.cs before you write any PDF-specific logic.

How Do You Create a Database Model for PDF Storage?

The foundation of any PDF upload system is a model class that maps to a database table. The following C# record captures the essential fields -- file name, content type, the raw binary data, and an upload timestamp.

public class PdfFileModel
{
    public int Id { get; set; }
    public string FileName { get; set; } = string.Empty;
    public string ContentType { get; set; } = "application/pdf";
    public byte[] FileData { get; set; } = Array.Empty<byte>();
    public DateTime UploadedDate { get; set; } = DateTime.UtcNow;
}
public class PdfFileModel
{
    public int Id { get; set; }
    public string FileName { get; set; } = string.Empty;
    public string ContentType { get; set; } = "application/pdf";
    public byte[] FileData { get; set; } = Array.Empty<byte>();
    public DateTime UploadedDate { get; set; } = DateTime.UtcNow;
}
$vbLabelText   $csharpLabel

FileData stores the PDF as a binary large object (BLOB). This approach keeps documents self-contained within the database, making backups simpler and queries straightforward. For high-volume scenarios or large files, consider storing only the file path in the database and writing the binary to a cloud storage bucket such as Azure Blob Storage or Amazon S3.

Configuring Entity Framework Core

Register the model with EF Core by adding a DbSet<PdfFileModel> property to your ApplicationDbContext:

using Microsoft.EntityFrameworkCore;

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options) { }

    public DbSet<PdfFileModel> PdfFiles { get; set; }
}
using Microsoft.EntityFrameworkCore;

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options) { }

    public DbSet<PdfFileModel> PdfFiles { get; set; }
}
$vbLabelText   $csharpLabel

Run dotnet ef migrations add InitialCreate followed by dotnet ef database update to create the schema. Entity Framework Core maps byte[] to a varbinary(max) column in SQL Server or a BLOB column in SQLite automatically -- no manual SQL is required.

How Do You Upload PDF Files in an ASP.NET Core Controller?

The controller action that handles uploads receives an IFormFile parameter from an HTML form using enctype="multipart/form-data". The action reads the stream into a MemoryStream, converts it to a byte array, and persists the result through Entity Framework Core.

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

public class PdfController : Controller
{
    private readonly ApplicationDbContext _context;

    public PdfController(ApplicationDbContext context)
    {
        _context = context;
    }

    [HttpPost]
    public async Task<IActionResult> Upload(IFormFile file)
    {
        if (file is null || file.Length == 0)
            return BadRequest("No file selected.");

        if (!file.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase))
            return BadRequest("Only PDF files are accepted.");

        using var stream = new MemoryStream();
        await file.CopyToAsync(stream);

        var pdfFile = new PdfFileModel
        {
            FileName = Path.GetFileName(file.FileName),
            ContentType = file.ContentType,
            FileData = stream.ToArray(),
            UploadedDate = DateTime.UtcNow
        };

        _context.PdfFiles.Add(pdfFile);
        await _context.SaveChangesAsync();

        return RedirectToAction(nameof(Index));
    }

    public async Task<IActionResult> Index()
    {
        var files = await _context.PdfFiles
            .Select(f => new { f.Id, f.FileName, f.UploadedDate })
            .ToListAsync();
        return View(files);
    }
}
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

public class PdfController : Controller
{
    private readonly ApplicationDbContext _context;

    public PdfController(ApplicationDbContext context)
    {
        _context = context;
    }

    [HttpPost]
    public async Task<IActionResult> Upload(IFormFile file)
    {
        if (file is null || file.Length == 0)
            return BadRequest("No file selected.");

        if (!file.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase))
            return BadRequest("Only PDF files are accepted.");

        using var stream = new MemoryStream();
        await file.CopyToAsync(stream);

        var pdfFile = new PdfFileModel
        {
            FileName = Path.GetFileName(file.FileName),
            ContentType = file.ContentType,
            FileData = stream.ToArray(),
            UploadedDate = DateTime.UtcNow
        };

        _context.PdfFiles.Add(pdfFile);
        await _context.SaveChangesAsync();

        return RedirectToAction(nameof(Index));
    }

    public async Task<IActionResult> Index()
    {
        var files = await _context.PdfFiles
            .Select(f => new { f.Id, f.FileName, f.UploadedDate })
            .ToListAsync();
        return View(files);
    }
}
$vbLabelText   $csharpLabel

Validating Uploaded Files

Always validate the content type before processing. Checking file.ContentType guards against users accidentally uploading non-PDF content. For stronger validation, read the first four bytes of the stream and verify the PDF magic number (%PDF). You should also enforce a maximum file size -- typically 10 MB for general document workflows -- by checking file.Length before copying the stream.

The HTML form that triggers this action needs two attributes: method="post" and enctype="multipart/form-data". Without the encoding type, browsers send the file name as plain text rather than the binary content. Add an <input type="file" name="file" accept=".pdf" /> element and a submit button inside the form tag pointing to /Pdf/Upload.

How to Upload and Download PDF Files in ASP .NET C# with IronPDF: Image 1 - UI showing the uploaded PDF

How Do You Add a Watermark to an Uploaded PDF Before Saving It?

Processing files server-side before storage is one of the most practical uses for IronPDF's watermarking features. You can stamp every incoming document with a "CONFIDENTIAL" label, a company logo, or a "DRAFT" notice before the bytes ever reach the database.

[HttpPost]
public async Task<IActionResult> UploadWithWatermark(IFormFile file)
{
    if (file is null || file.Length == 0)
        return BadRequest("No file selected.");

    using var stream = new MemoryStream();
    await file.CopyToAsync(stream);
    byte[] originalBytes = stream.ToArray();

    // Load the uploaded file into IronPDF
    var pdf = new IronPdf.PdfDocument(originalBytes);

    // Apply an HTML watermark centered on every page
    pdf.ApplyWatermark(
        "<h2 style='color:red;opacity:0.4'>CONFIDENTIAL</h2>",
        rotation: 45,
        opacity: 60,
        verticalAlignment: IronPdf.Editing.VerticalAlignment.Middle,
        horizontalAlignment: IronPdf.Editing.HorizontalAlignment.Center
    );

    var pdfFile = new PdfFileModel
    {
        FileName = Path.GetFileName(file.FileName),
        ContentType = "application/pdf",
        FileData = pdf.BinaryData,
        UploadedDate = DateTime.UtcNow
    };

    _context.PdfFiles.Add(pdfFile);
    await _context.SaveChangesAsync();

    return RedirectToAction(nameof(Index));
}
[HttpPost]
public async Task<IActionResult> UploadWithWatermark(IFormFile file)
{
    if (file is null || file.Length == 0)
        return BadRequest("No file selected.");

    using var stream = new MemoryStream();
    await file.CopyToAsync(stream);
    byte[] originalBytes = stream.ToArray();

    // Load the uploaded file into IronPDF
    var pdf = new IronPdf.PdfDocument(originalBytes);

    // Apply an HTML watermark centered on every page
    pdf.ApplyWatermark(
        "<h2 style='color:red;opacity:0.4'>CONFIDENTIAL</h2>",
        rotation: 45,
        opacity: 60,
        verticalAlignment: IronPdf.Editing.VerticalAlignment.Middle,
        horizontalAlignment: IronPdf.Editing.HorizontalAlignment.Center
    );

    var pdfFile = new PdfFileModel
    {
        FileName = Path.GetFileName(file.FileName),
        ContentType = "application/pdf",
        FileData = pdf.BinaryData,
        UploadedDate = DateTime.UtcNow
    };

    _context.PdfFiles.Add(pdfFile);
    await _context.SaveChangesAsync();

    return RedirectToAction(nameof(Index));
}
$vbLabelText   $csharpLabel

Watermark Configuration Options

IronPDF's ApplyWatermark method accepts an HTML string, which means your watermark can include any valid HTML and inline CSS -- gradients, custom fonts, rotated text, or even embedded SVG icons. The rotation parameter rotates the watermark across the page diagonal, while opacity controls transparency from 0 (invisible) to 100 (fully opaque).

Beyond watermarks, the same PdfDocument object exposes methods for adding headers and footers, stamping images, and editing existing form fields. You can chain multiple processing steps before calling pdf.BinaryData to retrieve the final byte array.

How to Upload and Download PDF Files in ASP .NET C# with IronPDF: Image 2 - PDF watermarked and then saved to the database

How Do You Download PDF Files Stored in the Database?

To serve a stored PDF back to the browser, retrieve the record by ID and return a FileResult. The ASP.NET Core File helper method sets the correct Content-Type header and triggers the browser's download dialog with the original file name.

public async Task<IActionResult> Download(int id)
{
    var pdfFile = await _context.PdfFiles.FindAsync(id);

    if (pdfFile is null)
        return NotFound();

    return File(pdfFile.FileData, pdfFile.ContentType, pdfFile.FileName);
}
public async Task<IActionResult> Download(int id)
{
    var pdfFile = await _context.PdfFiles.FindAsync(id);

    if (pdfFile is null)
        return NotFound();

    return File(pdfFile.FileData, pdfFile.ContentType, pdfFile.FileName);
}
$vbLabelText   $csharpLabel

Displaying a Download List in the View

The Index action retrieves all stored file records and passes them to a Razor view. A simple HTML table renders the file name, upload date, and a download anchor for each record.

<table class="content__data-table" data-content-table>
    <caption>Uploaded PDF Files</caption>
    <thead>
        <tr>
            <th>File Name</th>
            <th>Uploaded</th>
            <th>Action</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>@item.FileName</td>
                <td>@item.UploadedDate.ToString("yyyy-MM-dd HH:mm")</td>
                <td><a href="/Pdf/Download/@item.Id">Download</a></td>
            </tr>
        }
    </tbody>
</table>
<table class="content__data-table" data-content-table>
    <caption>Uploaded PDF Files</caption>
    <thead>
        <tr>
            <th>File Name</th>
            <th>Uploaded</th>
            <th>Action</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>@item.FileName</td>
                <td>@item.UploadedDate.ToString("yyyy-MM-dd HH:mm")</td>
                <td><a href="/Pdf/Download/@item.Id">Download</a></td>
            </tr>
        }
    </tbody>
</table>
$vbLabelText   $csharpLabel

The return File(bytes, contentType, fileName) overload sets both Content-Type: application/pdf and Content-Disposition: attachment; filename="..." headers. If you want the browser to open the PDF inline instead of prompting a download, use return File(bytes, contentType) without the third argument -- this omits the Content-Disposition: attachment instruction.

How to Upload and Download PDF Files in ASP .NET C# with IronPDF: Image 3 - List of stored PDF files

File System Storage as an Alternative

For large deployments, storing raw binary data in the database increases row sizes and can slow queries. An alternative is to write the file to a directory on disk -- or to a cloud provider -- and store only the relative path in the database. Replace FileData byte[] with FilePath string, write System.IO.File.WriteAllBytesAsync(path, bytes) during upload, and read System.IO.File.ReadAllBytesAsync(path) during download. Both paths converge on the same return File(...) call in the controller.

How Do You Generate PDF Documents On-Demand and Serve Them for Download?

You are not limited to serving pre-stored files. IronPDF's HTML-to-PDF conversion lets you generate documents dynamically from data at request time -- useful for invoices, reports, certificates, and data exports.

public IActionResult GenerateInvoice(int orderId)
{
    // Build HTML content from your data model
    string html = $@"
        <html>
        <body style='font-family: Arial, sans-serif; padding: 40px;'>
            <h1>Invoice #{orderId}</h1>
            <p>Generated: {DateTime.UtcNow:yyyy-MM-dd HH:mm} UTC</p>
            <table border='1' cellpadding='8'>
                <tr><th>Item</th><th>Qty</th><th>Price</th></tr>
                <tr><td>IronPDF License</td><td>1</td><td>$749</td></tr>
            </table>
        </body>
        </html>";

    var renderer = new IronPdf.ChromePdfRenderer();
    using var pdf = renderer.RenderHtmlAsPdf(html);

    return File(pdf.BinaryData, "application/pdf", $"invoice-{orderId}.pdf");
}
public IActionResult GenerateInvoice(int orderId)
{
    // Build HTML content from your data model
    string html = $@"
        <html>
        <body style='font-family: Arial, sans-serif; padding: 40px;'>
            <h1>Invoice #{orderId}</h1>
            <p>Generated: {DateTime.UtcNow:yyyy-MM-dd HH:mm} UTC</p>
            <table border='1' cellpadding='8'>
                <tr><th>Item</th><th>Qty</th><th>Price</th></tr>
                <tr><td>IronPDF License</td><td>1</td><td>$749</td></tr>
            </table>
        </body>
        </html>";

    var renderer = new IronPdf.ChromePdfRenderer();
    using var pdf = renderer.RenderHtmlAsPdf(html);

    return File(pdf.BinaryData, "application/pdf", $"invoice-{orderId}.pdf");
}
$vbLabelText   $csharpLabel

Rendering Options for On-Demand PDFs

ChromePdfRenderer produces pixel-accurate output using the same Chromium rendering engine that powers Google Chrome. This means any CSS you can display in a browser -- flexbox layouts, grid, custom fonts, SVG charts -- renders correctly in the generated PDF. You can set paper size, margins, and orientation through the RenderingOptions property before calling RenderHtmlAsPdf.

For more complex documents, pass a full URL to RenderUrlAsPdf instead of an HTML string. IronPDF will load the page in a headless browser, execute JavaScript, apply styles, and convert the fully rendered DOM to PDF. Review the HTML to PDF conversion guide for the complete set of rendering options, including custom headers, footers, and page-numbering tokens.

How to Upload and Download PDF Files in ASP .NET C# with IronPDF: Image 4 - Generated example PDF

How Do You Merge Multiple PDF Files in ASP.NET Core?

Beyond single-file operations, you may need to combine several uploaded documents into one. IronPDF's PDF merging capability accepts a list of PdfDocument objects and returns a single merged file.

public async Task<IActionResult> MergeAll()
{
    var allFiles = await _context.PdfFiles.ToListAsync();

    if (allFiles.Count < 2)
        return BadRequest("At least two files are required for merging.");

    var documents = allFiles
        .Select(f => new IronPdf.PdfDocument(f.FileData))
        .ToList();

    using var merged = IronPdf.PdfDocument.Merge(documents);

    return File(merged.BinaryData, "application/pdf", "merged.pdf");
}
public async Task<IActionResult> MergeAll()
{
    var allFiles = await _context.PdfFiles.ToListAsync();

    if (allFiles.Count < 2)
        return BadRequest("At least two files are required for merging.");

    var documents = allFiles
        .Select(f => new IronPdf.PdfDocument(f.FileData))
        .ToList();

    using var merged = IronPdf.PdfDocument.Merge(documents);

    return File(merged.BinaryData, "application/pdf", "merged.pdf");
}
$vbLabelText   $csharpLabel

Splitting Pages from a PDF

The reverse operation -- extracting a subset of pages -- uses CopyPages. Load a PdfDocument from the stored bytes, call source.CopyPages(startIndex, endIndex) with zero-based page indices, and return the resulting PdfDocument.BinaryData as a FileResult. This pattern is useful for pagination previews, splitting multi-section reports, or extracting a cover page for thumbnail generation. You can also apply digital signatures to the merged or split output before serving it to users.

How Do You Handle Large File Uploads Securely?

Large PDF files require additional configuration at the ASP.NET Core middleware level. By default, the request body size limit is set to approximately 28 MB. To raise it, call builder.Services.Configure<FormOptions> to set MultipartBodyLengthLimit and builder.WebHost.ConfigureKestrel to set Limits.MaxRequestBodySize -- both to your desired byte count, such as 50 * 1024 * 1024 for 50 MB -- in Program.cs before builder.Build().

Beyond size limits, apply these security practices to every upload endpoint: validate the content type header, inspect the first bytes of the stream for the %PDF magic number, scan for embedded scripts using IronPDF's document inspection API, and store processed files outside the web root so they are never served as static content directly. The ASP.NET Core security documentation covers additional hardening techniques including antiforgery token validation and virus scanning integration.

Streaming Large Files to Avoid Memory Pressure

When files exceed 10 MB, reading the entire stream into a MemoryStream before processing can spike memory usage significantly. Use IronPdf.PdfDocument.FromStream to load directly from the request stream where possible, or write to a temporary file path and load from disk:

string tempPath = Path.GetTempFileName();
await using (var fs = System.IO.File.Create(tempPath))
{
    await file.CopyToAsync(fs);
}

using var pdf = new IronPdf.PdfDocument(tempPath);
// process...
System.IO.File.Delete(tempPath);
string tempPath = Path.GetTempFileName();
await using (var fs = System.IO.File.Create(tempPath))
{
    await file.CopyToAsync(fs);
}

using var pdf = new IronPdf.PdfDocument(tempPath);
// process...
System.IO.File.Delete(tempPath);
$vbLabelText   $csharpLabel

This pattern keeps heap allocations low and works well with background processing queues where files are processed asynchronously after the HTTP response has already been sent. Explore the IronPDF documentation for additional async processing patterns.

What Are Your Next Steps?

You now have a complete foundation for uploading, processing, storing, and downloading PDF files in an ASP.NET Core MVC application backed by IronPDF. From here, consider the following directions to extend the workflow.

Extend processing capabilities. IronPDF supports filling and reading PDF form fields, extracting text and images with the PDF text extraction API, and converting PDF pages to images for thumbnail previews. Each of these features integrates into the same controller pattern shown above.

Add digital signatures. Sign every generated or uploaded document with a digital signature using an X.509 certificate before storing it. Signed PDFs carry tamper-evident metadata that satisfies many compliance requirements.

Scale storage to the cloud. Replace the local byte[] database column with an Azure Blob Storage or Amazon S3 reference. Upload the processed bytes to cloud storage after watermarking and save only the URI in the database -- this dramatically reduces database row sizes and enables CDN delivery.

Start a free trial. Visit the IronPDF trial license page to get a 30-day evaluation key with full feature access. You can also browse the complete IronPDF features overview to understand the full scope of PDF capabilities available in your .NET application, or consult the pricing and licensing page when you are ready for production deployment.

Frequently Asked Questions

How can I upload PDF files in an ASP.NET Core MVC application?

To upload PDF files in an ASP.NET Core MVC application, you can use the IFormFile interface to capture file data from a form and then process it server-side before saving, possibly with the help of IronPDF for further PDF manipulation.

What is the best way to download PDF files in ASP.NET?

The best way to download PDF files in ASP.NET is to use the FileResult action in your controller. IronPDF can assist in generating and modifying PDFs server-side to ensure they are ready for download.

Can I store PDF files in a database using ASP.NET?

Yes, you can store PDF files in a database using ASP.NET by converting the file to a byte array and saving it as a binary large object (BLOB). IronPDF can help in processing the PDF before storage.

How does IronPDF help with watermarking PDFs in ASP.NET?

IronPDF provides functionality to easily add text or image watermarks to PDFs, which can be integrated into your ASP.NET application to modify documents before download or storage.

What are the advantages of using EF Core for PDF storage?

EF Core allows for efficient object-relational mapping, making it easier to manage PDF storage and retrieval in a structured and scalable manner within your ASP.NET application.

Is it possible to manipulate PDF content in ASP.NET applications?

Yes, with IronPDF, you can manipulate PDF content, including editing text, images, and metadata, which can be useful for customizing documents before they are served to users.

How can I handle file uploads securely in ASP.NET?

To handle file uploads securely in ASP.NET, you should validate file types, limit file sizes, and store them in secure locations. Using libraries like IronPDF can also help ensure the integrity of the PDF files themselves.

What are common challenges when working with PDFs in web applications?

Common challenges include ensuring file compatibility, managing large file sizes, and maintaining document integrity. IronPDF helps overcome these by providing robust tools for PDF creation and manipulation.

Can I convert different file types to PDF in ASP.NET?

Yes, IronPDF allows you to convert various file types, such as HTML or image files, into PDFs seamlessly within your ASP.NET application.

What is the role of Model-View-Controller (MVC) in handling PDFs in ASP.NET?

The MVC pattern helps in organizing the code for handling PDFs by separating data handling (Model), user interface (View), and application logic (Controller), making it easier to manage and extend PDF functionalities.

Curtis Chau
Technical Writer

Curtis Chau holds a Bachelor’s degree in Computer Science (Carleton University) and specializes in front-end development with expertise in Node.js, TypeScript, JavaScript, and React. Passionate about crafting intuitive and aesthetically pleasing user interfaces, Curtis enjoys working with modern frameworks and creating well-structured, visually appealing manuals.

...

Read More

Iron Support Team

We're online 24 hours, 5 days a week.
Chat
Email
Call Me