Skip to footer content
USING IRONPDF

How to Retrieve PDF Files from a Database in ASP.NET Using C#

Retrieving a PDF file from a database in ASP.NET using C# requires three steps: query the database table for the binary BLOB column, load the bytes into a PdfDocument object using IronPDF, and return the bytes to the browser via a FileContentResult or File() response. IronPDF handles the rendering, watermarking, and security features so you can focus on the data-access logic.

How Do You Install IronPDF for ASP.NET?

Before writing any PDF retrieval code, add IronPDF to your project through the NuGet Package Manager:

Install-Package IronPdf
dotnet add package IronPdf
Install-Package IronPdf
dotnet add package IronPdf
SHELL

After installation, set your license key in Program.cs or appsettings.json before calling any IronPDF method:

IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
$vbLabelText   $csharpLabel

IronPDF supports .NET 10, .NET 8, .NET 6, and .NET Framework 4.6.2+. It works on Windows, Linux, and macOS without requiring any additional dependencies or headless browser installations. A free trial license is available for evaluation.

How Do You Set Up the SQL Server Database Table?

The most common approach is to store PDF files as binary data in a SQL Server VARBINARY(MAX) column. This keeps the document and its metadata together in a single table, simplifies backup, and avoids file-system path management.

Use the following SQL script to create the storage table:

// SQL Server table definition (run this in SSMS or via EF migrations)
// CREATE TABLE PdfDocuments (
//     Id INT IDENTITY(1,1) PRIMARY KEY,
//     FileName NVARCHAR(255) NOT NULL,
//     FileContent VARBINARY(MAX) NOT NULL,
//     UploadedAt DATETIME2 DEFAULT GETUTCDATE()
// );
// SQL Server table definition (run this in SSMS or via EF migrations)
// CREATE TABLE PdfDocuments (
//     Id INT IDENTITY(1,1) PRIMARY KEY,
//     FileName NVARCHAR(255) NOT NULL,
//     FileContent VARBINARY(MAX) NOT NULL,
//     UploadedAt DATETIME2 DEFAULT GETUTCDATE()
// );
$vbLabelText   $csharpLabel

Once the table exists, configure the connection string in appsettings.json:

// appsettings.json snippet (not C# -- shown as reference)
// "ConnectionStrings": {
//   "DefaultConnection": "Server=localhost;Database=PdfStorage;Integrated Security=True;"
// }
// appsettings.json snippet (not C# -- shown as reference)
// "ConnectionStrings": {
//   "DefaultConnection": "Server=localhost;Database=PdfStorage;Integrated Security=True;"
// }
$vbLabelText   $csharpLabel

Register the connection string via dependency injection in Program.cs:

using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSingleton<IConfiguration>(builder.Configuration);

IronPdf.License.LicenseKey = builder.Configuration["IronPdf:LicenseKey"];

var app = builder.Build();
app.MapControllers();
app.Run();
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSingleton<IConfiguration>(builder.Configuration);

IronPdf.License.LicenseKey = builder.Configuration["IronPdf:LicenseKey"];

var app = builder.Build();
app.MapControllers();
app.Run();
$vbLabelText   $csharpLabel

How Do You Retrieve a PDF from SQL Server in ASP.NET Core?

The retrieval pattern follows three steps: open a connection, execute a parameterized SELECT query, and read the binary column into a byte[]. IronPDF then loads that array into a PdfDocument object for optional processing before streaming to the client.

Building the API Controller

Create a controller that exposes GET endpoints for both inline display and file download:

using IronPdf;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;

[ApiController]
[Route("api/[controller]")]
public class PdfController : ControllerBase
{
    private readonly string _connectionString;

    public PdfController(IConfiguration configuration)
    {
        _connectionString = configuration.GetConnectionString("DefaultConnection")
            ?? throw new InvalidOperationException("Connection string not found.");
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> GetPdf(int id)
    {
        byte[] pdfBytes = await RetrievePdfBytesAsync(id);
        if (pdfBytes is null || pdfBytes.Length == 0)
            return NotFound("PDF document not found.");

        // Load into IronPDF for validation or optional modification
        using var pdfDocument = new PdfDocument(pdfBytes);

        // Inline display -- browser opens PDF viewer
        Response.Headers.Append("Content-Disposition", "inline; filename=\"document.pdf\"");
        return File(pdfDocument.BinaryData, "application/pdf");
    }

    private async Task<byte[]> RetrievePdfBytesAsync(int documentId)
    {
        await using var connection = new SqlConnection(_connectionString);
        await connection.OpenAsync();

        const string query = "SELECT FileContent FROM PdfDocuments WHERE Id = @Id";
        await using var command = new SqlCommand(query, connection);
        command.Parameters.AddWithValue("@Id", documentId);

        var result = await command.ExecuteScalarAsync();
        return result as byte[] ?? Array.Empty<byte>();
    }
}
using IronPdf;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;

[ApiController]
[Route("api/[controller]")]
public class PdfController : ControllerBase
{
    private readonly string _connectionString;

    public PdfController(IConfiguration configuration)
    {
        _connectionString = configuration.GetConnectionString("DefaultConnection")
            ?? throw new InvalidOperationException("Connection string not found.");
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> GetPdf(int id)
    {
        byte[] pdfBytes = await RetrievePdfBytesAsync(id);
        if (pdfBytes is null || pdfBytes.Length == 0)
            return NotFound("PDF document not found.");

        // Load into IronPDF for validation or optional modification
        using var pdfDocument = new PdfDocument(pdfBytes);

        // Inline display -- browser opens PDF viewer
        Response.Headers.Append("Content-Disposition", "inline; filename=\"document.pdf\"");
        return File(pdfDocument.BinaryData, "application/pdf");
    }

    private async Task<byte[]> RetrievePdfBytesAsync(int documentId)
    {
        await using var connection = new SqlConnection(_connectionString);
        await connection.OpenAsync();

        const string query = "SELECT FileContent FROM PdfDocuments WHERE Id = @Id";
        await using var command = new SqlCommand(query, connection);
        command.Parameters.AddWithValue("@Id", documentId);

        var result = await command.ExecuteScalarAsync();
        return result as byte[] ?? Array.Empty<byte>();
    }
}
$vbLabelText   $csharpLabel

This controller uses parameterized queries to prevent SQL injection and await using to dispose SqlConnection and SqlCommand correctly. The PdfDocument class validates the byte array and exposes the BinaryData property for streaming.

Returning a Named File for Download

When users need to save the document rather than view it inline, set the Content-Disposition header to attachment and pass the original filename:

[HttpGet("download/{id}")]
public async Task<IActionResult> DownloadPdf(int id)
{
    await using var connection = new SqlConnection(_connectionString);
    await connection.OpenAsync();

    const string query = "SELECT FileName, FileContent FROM PdfDocuments WHERE Id = @Id";
    await using var command = new SqlCommand(query, connection);
    command.Parameters.AddWithValue("@Id", documentId);

    await using var reader = await command.ExecuteReaderAsync();
    if (!await reader.ReadAsync())
        return NotFound("Document not found.");

    var fileName = reader.GetString(reader.GetOrdinal("FileName"));
    var pdfBytes = (byte[])reader["FileContent"];

    using var pdfDocument = new PdfDocument(pdfBytes);
    return File(pdfDocument.BinaryData, "application/pdf", fileName);
}
[HttpGet("download/{id}")]
public async Task<IActionResult> DownloadPdf(int id)
{
    await using var connection = new SqlConnection(_connectionString);
    await connection.OpenAsync();

    const string query = "SELECT FileName, FileContent FROM PdfDocuments WHERE Id = @Id";
    await using var command = new SqlCommand(query, connection);
    command.Parameters.AddWithValue("@Id", documentId);

    await using var reader = await command.ExecuteReaderAsync();
    if (!await reader.ReadAsync())
        return NotFound("Document not found.");

    var fileName = reader.GetString(reader.GetOrdinal("FileName"));
    var pdfBytes = (byte[])reader["FileContent"];

    using var pdfDocument = new PdfDocument(pdfBytes);
    return File(pdfDocument.BinaryData, "application/pdf", fileName);
}
$vbLabelText   $csharpLabel

Passing fileName as the third argument to File() sets the Content-Disposition header to attachment automatically. ASP.NET Core handles the correct quoting of filenames that contain spaces.

How Do You Add a Watermark to a Retrieved PDF?

One of the most practical post-retrieval operations is stamping a watermark on every page before serving the document. This is useful for confidential reports, draft documents, or any file that needs a visible security marking.

Applying an HTML Watermark with IronPDF

IronPDF's watermark API accepts any HTML string, which means you can style the watermark text using inline CSS. Set the opacity low enough that the underlying content remains readable:

[HttpGet("watermarked/{id}")]
public async Task<IActionResult> GetWatermarkedPdf(int id)
{
    byte[] pdfBytes = await RetrievePdfBytesAsync(id);
    if (pdfBytes is null || pdfBytes.Length == 0)
        return NotFound("PDF document not found.");

    using var pdfDocument = new PdfDocument(pdfBytes);

    // HTML watermark applied to every page
    string watermarkHtml = "<h2 style='color:red; opacity:0.4; font-family:Arial;'>CONFIDENTIAL</h2>";
    pdfDocument.ApplyWatermark(
        watermarkHtml,
        rotation: 30,
        verticalAlignment: VerticalAlignment.Middle,
        horizontalAlignment: HorizontalAlignment.Center
    );

    return File(pdfDocument.BinaryData, "application/pdf");
}
[HttpGet("watermarked/{id}")]
public async Task<IActionResult> GetWatermarkedPdf(int id)
{
    byte[] pdfBytes = await RetrievePdfBytesAsync(id);
    if (pdfBytes is null || pdfBytes.Length == 0)
        return NotFound("PDF document not found.");

    using var pdfDocument = new PdfDocument(pdfBytes);

    // HTML watermark applied to every page
    string watermarkHtml = "<h2 style='color:red; opacity:0.4; font-family:Arial;'>CONFIDENTIAL</h2>";
    pdfDocument.ApplyWatermark(
        watermarkHtml,
        rotation: 30,
        verticalAlignment: VerticalAlignment.Middle,
        horizontalAlignment: HorizontalAlignment.Center
    );

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

The ApplyWatermark method accepts standard HTML and CSS, so you have full control over font, color, opacity, and position. The watermark is applied to all pages in the document automatically. For additional PDF manipulation features, including stamping images, adding headers and footers, or merging multiple documents, see the IronPDF documentation.

How Do You Store Uploaded PDFs Back in SQL Server?

Completing the round-trip requires an upload endpoint that reads incoming form files and writes them to the database. This pairs with the retrieval endpoints above to form a complete document management system:

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

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

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

    // Validate using IronPDF before storage
    using var pdfDocument = new PdfDocument(pdfBytes);

    await using var connection = new SqlConnection(_connectionString);
    await connection.OpenAsync();

    const string insertQuery = @"
        INSERT INTO PdfDocuments (FileName, FileContent)
        VALUES (@FileName, @FileContent);
        SELECT SCOPE_IDENTITY();";

    await using var command = new SqlCommand(insertQuery, connection);
    command.Parameters.AddWithValue("@FileName", file.FileName);
    command.Parameters.AddWithValue("@FileContent", pdfDocument.BinaryData);

    var newId = Convert.ToInt32(await command.ExecuteScalarAsync());
    return Ok(new { id = newId, fileName = file.FileName });
}
[HttpPost("upload")]
public async Task<IActionResult> UploadPdf(IFormFile file)
{
    if (file is null || file.Length == 0)
        return BadRequest("No file uploaded.");

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

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

    // Validate using IronPDF before storage
    using var pdfDocument = new PdfDocument(pdfBytes);

    await using var connection = new SqlConnection(_connectionString);
    await connection.OpenAsync();

    const string insertQuery = @"
        INSERT INTO PdfDocuments (FileName, FileContent)
        VALUES (@FileName, @FileContent);
        SELECT SCOPE_IDENTITY();";

    await using var command = new SqlCommand(insertQuery, connection);
    command.Parameters.AddWithValue("@FileName", file.FileName);
    command.Parameters.AddWithValue("@FileContent", pdfDocument.BinaryData);

    var newId = Convert.ToInt32(await command.ExecuteScalarAsync());
    return Ok(new { id = newId, fileName = file.FileName });
}
$vbLabelText   $csharpLabel

Using PdfDocument to validate before storage ensures that only parseable, well-formed PDF files enter the database. If the byte array is corrupted or truncated, IronPDF throws an exception that you can catch and return as a 400 Bad Request response.

What Are the Key Tables and Column Types for PDF Storage?

The schema you use affects query performance and storage efficiency. The table below shows the recommended column configuration for SQL Server:

Recommended SQL Server schema for PDF BLOB storage
Column Data Type Purpose
Id INT IDENTITY Primary key, auto-increment
FileName NVARCHAR(255) Original filename for download headers
FileContent VARBINARY(MAX) Raw PDF binary data (BLOB)
ContentType NVARCHAR(100) MIME type, e.g., application/pdf
FileSizeBytes BIGINT Stored size for quota management
UploadedAt DATETIME2 UTC timestamp for auditing
UploadedBy NVARCHAR(100) User identity for access control

For large-scale systems where SQL Server file streaming is needed, Microsoft documents the FILESTREAM feature as an alternative that stores large BLOBs on the file system while remaining queryable through T-SQL. However, for most ASP.NET applications serving documents up to a few hundred megabytes, VARBINARY(MAX) in-row storage works well and simplifies deployment.

How Do You Handle Errors and Optimize Performance?

Reliable PDF retrieval in production requires error handling at each layer -- database, IronPDF, and HTTP response. The table below summarizes the key practices:

Error handling and performance patterns for PDF retrieval
Concern Recommendation Reason
Connection disposal await using statements Prevents connection pool exhaustion
PdfDocument disposal using statements Releases unmanaged memory promptly
SQL injection Parameterized queries only Prevents malicious input from altering queries
File type validation Check MIME type and magic bytes Blocks non-PDF uploads before storage
Large file handling Stream response with FileStreamResult Avoids loading entire file into server memory
Caching Use IMemoryCache or IDistributedCache Reduces repeated database round-trips
Async operations async/await throughout Keeps threads free during disk and network waits

For connection string management, store the value in appsettings.json and never hardcode it in source files. Use ASP.NET Core's built-in secrets management during local development, and Azure Key Vault or AWS Secrets Manager in production. Never commit connection strings or license keys to source control.

When serving large PDF files, consider returning a FileStreamResult backed by a MemoryStream rather than loading the entire byte array into memory. For very large documents -- over 100 MB -- the SQL Server FILESTREAM API enables chunked streaming directly from the file system.

Caching Frequently Accessed Documents

If certain PDF files are requested repeatedly -- for example, a terms-and-conditions document or a product catalog -- caching the byte array in IMemoryCache avoids repeated database round-trips. Register IMemoryCache in Program.cs with builder.Services.AddMemoryCache(), then inject it into the controller and check the cache before querying the database. Set an absolute expiry that matches the expected update frequency of the documents. When a document is updated, remove the cached entry by key so the next request fetches the new version.

For distributed scenarios -- such as a load-balanced deployment with multiple server instances -- replace IMemoryCache with IDistributedCache backed by Redis or SQL Server. ASP.NET Core's distributed cache abstractions keep the controller code almost identical; only the registration in Program.cs changes.

How Do You Cross-Platform Deploy PDF Retrieval with IronPDF?

IronPDF runs on Linux, Windows, and macOS without requiring a separate Chromium installation or any configuration changes. The same NuGet package targets all platforms, so your PDF controller works identically whether you deploy to:

  • Windows Server with IIS
  • Ubuntu containers on Docker or Kubernetes
  • Azure App Service (Linux or Windows)
  • AWS Elastic Beanstalk

Deploying to Docker and Linux

For Docker deployments, add the IronPDF dependencies to your Dockerfile. The IronPDF Linux documentation provides the exact apt packages required for Debian and Alpine base images. A typical multi-stage Dockerfile installs the OS dependencies in the runtime image stage, then copies the published ASP.NET application on top. When using Azure, the Azure deployment guide covers App Service configuration, including the memory and CPU settings that support PDF rendering at scale.

Because IronPDF bundles its own Chromium-based rendering engine, you do not need to install a separate browser on the server. This simplifies Linux container setups considerably compared to solutions that require a system-level browser. The IronPDF team tests against the most common Linux base images with every release, so you can trust that an Alpine or Debian container will work out of the box.

Using Entity Framework Core Instead of ADO.NET

IronPDF also integrates with Entity Framework Core as an alternative to raw ADO.NET. If your project already uses EF Core, you can map the FileContent column to a byte[] property on a model class and let EF handle the query generation. This approach reduces boilerplate significantly and makes it easier to add filtering, pagination, and auditing behavior through EF's LINQ provider.

The trade-off is that EF Core loads the entire BLOB into memory as part of the entity graph. For very large PDF files, consider using raw ADO.NET or EF Core's FromSql method with a projection that selects only the byte array column rather than the full entity.

What Are Your Next Steps?

Retrieving PDF files from a SQL Server database in ASP.NET Core using C# and IronPDF follows a clear pattern: query the BLOB column with a parameterized SELECT, load the bytes into a PdfDocument, and return the binary data with the correct Content-Disposition header. IronPDF adds the ability to validate, watermark, merge, or secure documents before they leave your server.

To go further with IronPDF's document management capabilities, explore these resources:

Start with a free IronPDF trial license to test all features without restriction. The trial produces watermarked output; remove the watermark by applying a paid license key. Full API reference documentation and code samples are available on the IronPDF website for every method used in this article.

If your project is already using Entity Framework Core, the IronPDF EF Core integration guide shows how to replace raw ADO.NET with entity models while keeping the same IronPDF processing pipeline. For teams working with .NET 10 and the latest ASP.NET Core features, the same patterns described here work without modification -- IronPDF supports every active .NET LTS and STS release.

Review the IronPDF pricing page to find the right license tier for your deployment. A single developer license covers local development and testing; redistribution licenses are available for SaaS products and on-premises deployments with multiple servers.

Frequently Asked Questions

What is IronPDF?

IronPDF is a .NET library that allows developers to create, edit, and extract content from PDF files in C# applications.

How can I retrieve a PDF file from a database using ASP.NET?

To retrieve a PDF file from a database in ASP.NET, you can use C# code to query the database and read the PDF data into a byte array. This byte array can then be used with IronPDF to render or manipulate the PDF as needed.

Why should I use IronPDF for handling PDFs in my ASP.NET application?

IronPDF offers a wide set of features for handling PDFs, including PDF generation, conversion from HTML, and manipulation. It integrates with ASP.NET and provides an easy-to-use API for working with PDFs.

What are the prerequisites for using IronPDF in ASP.NET?

To use IronPDF in ASP.NET, you need to have a .NET development environment set up, such as Visual Studio, and include the IronPDF library in your project via NuGet package manager.

Can IronPDF be used to edit existing PDF files?

Yes, IronPDF can be used to edit existing PDF files. It allows for modifications such as adding text or images, merging documents, and more.

Is it possible to convert HTML to PDF with IronPDF?

Yes, IronPDF can convert HTML content directly into PDF format, making it easy to generate PDFs from web pages or other HTML content.

How do I handle PDF security features using IronPDF?

IronPDF supports various security features for PDFs, including password protection and setting permissions to control document access and editing.

What types of databases are compatible with IronPDF for PDF retrieval?

IronPDF can work with any database that can store binary data, such as SQL Server, MySQL, or PostgreSQL, to retrieve and manipulate PDF files.

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