Tell me if you’ve heard this one before. You spend months working with UI and UX experts, building what you think is the most optimal means of displaying business information. You sit in a meeting with your stakeholders, demoing what you created. Everything feels great, but then silence fills the room. A lone stakeholder raises their voice, “This is all great, but can we get it in a PDF?”.
The Portable Document Format (PDF) is in a heated race, with Excel spreadsheets as the most valuable file format in business today. As a developer, you will inevitably be asked to compress everything into a file that business individuals can share via email. Don’t fight it, but embrace it.
In this post, we’ll see the two approaches to transmitting PDF files through ASP.NET Core to get two different behaviors: In-browser preview and downloads.
Generating a PDF with QuestPDF
While this post isn’t a tutorial for generating PDFs, you’ll need a way to generate PDFs to follow along. I recommend using the QuestPDF library. The API is easy to get started with and will have you generating PDF documents in no time. Also, their licensing costs are reasonable, ranging from a free community license to an Enterprise license. That said, feel free to substitute any PDF-generating method you prefer.
Here is the code for my PDF generation.
private static byte[] GetPdfDocuments()
{
var pdf =
Document.Create(container =>
{
container.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(2, Unit.Centimetre);
page.PageColor(Colors.White);
page.DefaultTextStyle(x => x.FontSize(20));
page.Header()
.Text("Hello PDF!")
.SemiBold().FontSize(36)
.FontColor(Colors.Blue.Medium);
page.Content()
.PaddingVertical(1, Unit.Centimetre)
.Column(x =>
{
x.Spacing(20);
x
.Item()
.Text(Placeholders.LoremIpsum());
x
.Item()
.Image(Placeholders.Image(200, 100));
});
page.Footer()
.AlignCenter()
.Text(x =>
{
x.Span("Page ");
x.CurrentPageNumber();
});
});
}).GeneratePdf();
return pdf;
}
OK, let’s move on to one of the first ways of transmitting a PDF to your user: previewing the file within the browser.
Previewing a PDF File in the Browser
In this example, I’ll be using ASP.NET Core MVC, but this approach should work with Razor Pages and even Minimal APIs. Adjust the code accordingly.
public IActionResult Show()
{
var pdf = GetPdfDocuments();
return File(pdf, "application/pdf");
}
Next, you’ll want to add the button in your view to trigger this MVC action.
<a class="btn btn-primary"
asp-controller="Files"
asp-action="Show"
target="_blank">
Preview PDF
</a>
That’s it! Clicking the Preview button will trigger your browser to open the file in a new tab. But you may be asking yourself, “Why does this work?” Let’s look at the response headers of the HTTP request.
Content-Length: 78127
Content-Type: application/pdf
Date: Wed, 06 Sep 2023 14:17:30 GMT
Server: Kestrel
A client (web browser) uses the content type of application/pdf
to determine if a default viewing application is
associated with the MIME type. Since the PDF is a standard format, most modern browsers can render them in-app (sorry,
Adobe Acrobat Reader). The user can now view the file as if it were another tab.
Note: It’s essential to recognize that users can also change their preferences regarding how files are handled, so this can behave differently based on an individual’s local setup.
What if you don’t want this behavior and want to force a download?
Downloading a PDF File in the Browser
If you want to let folks download files rather than having the client open the file in the browser, it’s as simple as adding another argument to the previous code.
public IActionResult Download()
{
var pdf = GetPdfDocuments();
return File(pdf, "application/pdf", "download.pdf");
}
Then, the button in your view is similar to the previous example.
<a class="btn btn-primary"
asp-controller="Files"
asp-action="Download">
Download PDF
</a>
Now, why does this download the PDF rather than preview the file?
Let’s look at the HTTP response headers to see what’s different.
Content-Disposition: attachment; filename=download.pdf; filename*=UTF-8''download.pdf
Content-Length: 78109
Content-Type: application/pdf
Date: Wed, 06 Sep 2023 14:17:30 GMT
Server: Kestrel
You can see a Content-Disposition
response header with the value of attachment
and a filename
. This tells the
browser that the server intends the client to download the file rather than attempt to render it as a document.
Conclusion
While the focus of this blog post was PDFs, this applies to any document type. If the user has a default application that can preview a file, then excluding the filename will attempt to open and preview the document. For PDFs, most browsers can render them in-app without additional applications. For downloading files, you only need to remember to give the file a name.
I hope this post was informative to you, and as always, thanks for sharing my posts with colleagues and friends. Cheers.