germankiwi
germankiwi

Reputation: 1132

ASP.NET Core (on IIS) PDF files missing last bytes

I have an ASP.NET Core 2.1 application hosted on IIS (Framework-Dependent), which generates PDF files using Rotativa.AspNetCore. The server has the ".NET Core 2.2 Runtime & Hosting Bundle for Windows (v2.2.3)" installed.

This works fine locally, but when deployed and hosted within IIS, the file returned appears to be incomplete (IIS log sc-bytes is less than it should be) and the browser cannot display the PDF file (Chrome shows "Error / Failed to load PDF document.", Firefox shows "The connection was reset" and Postman shows "Could not get any response").

=== UPDATE ===

Interestingly, the live site hosted on a different server didn't show the same issue, neither did another ASP.NET Core 2.1 app hosted on the same server.

I finally found that simply using HTTPS solved the issue and it is now serving the files reliably.

=== END UPDATE ===

Stdout logging on debug level did not have any useful information, neither did the Event viewer. It looks like the server thinks it served the request successfully, but somehow the connection was closed before the response was successfully sent to the browser.

As a last resort, I tried to save the generated file on the file system and just redirect to the file, but even that has the same issue.

My original code looked something like this:

public IActionResult MyExport()
{
    var myViewModel = new MyViewModel();
    return new ViewAsPdf("~/Views/PdfExport/MyExport.cshtml", myViewModel)
    {
        PageSize = Size.A4,
        PageOrientation = Orientation.Portrait,
        PageMargins = { Left = 1, Right = 1 }
    };
}

After attempting to save to disk and redirecting to the file, my code is basically this:

public async Task<IActionResult> MyExport()
{
    var myViewModel = new MyViewModel();
    var pdf = new ViewAsPdf("~/Views/PdfExport/MyExport.cshtml", myViewModel)
    {
        PageSize = Size.A4,
        PageOrientation = Orientation.Portrait,
        PageMargins = { Left = 1, Right = 1 }
    };
    var pdfBytes = await pdf.BuildFile(ControllerContext);
    var path = $"/_generated/xyz.pdf";
    System.IO.File.WriteAllBytes($"{_hostingEnvironment.WebRootPath}{path}", pdfBytes);
    return Redirect(path);
}

The file is successfully generated in /wwwroot/_generated/xyz.pdf, but somehow even browsing directly to myurl.com/_generated/xyz.pdf still suffers the same problem and seems to only return part of the content.
I have an example where the header Content-Length is 88185 bytes but sc-bytes in IIS logs is only 63852 bytes

The generated PDF files are pretty small (eg 87kB) and I haven't experienced the same issue with any static files that are bigger (images, js, etc).

EDIT
Even an uploaded PDF file fails to serve correctly, not just the generated ones.

What am I missing? Is there something in the Startup I need to configure to successfully serve PDF files?

Upvotes: 0

Views: 789

Answers (2)

germankiwi
germankiwi

Reputation: 1132

It turns out, after simply using HTTPS rather than HTTP, the PDF files started to be served reliably.
Bit of a face palm moment...

Upvotes: 0

Hirasawa Yui
Hirasawa Yui

Reputation: 1296

save to disk and redirecting to the file <- this! You should never let user just browse your file system. What if he navigates to myurl.com/_generated../../web.config or whatever and gets your configuration file? You should definitely use streams instead. Like:

response.Content = new StreamContent(new FileStream(pathToFile,FileMode.Open));

Upvotes: 1

Related Questions