Recently, I needed an ActionResult implementation to return the Pdf documents from my Controller Action to MVC views and it tooks few minutes to build the functionality on the existing FileResult.
I decided to build a base class PdfResult to abstract the ContentType implementation as well as it will also serve as a common return type from the action method irrespective of derived implementation like Content, Path etc.
PdfResult Base class
/// /// PdfResult /// Represents a base class that is used to send PDF file content to the response. public abstract class PdfResult : FileResult { #region Constructor /// /// Initializes a new instance of the FileResult class. /// public PdfResult() : base(System.Net.Mime.MediaTypeNames.Application.Pdf) { } #endregion }
PdfStreamResult It’s one of the implementation class for PdfResult to return the Pdf content as stream on HttpResponse Stream.
/// /// PdfStreamResult /// Sends binary content to the response by using a Stream instance. /// public class PdfStreamResult : PdfResult { // Fields private const int _bufferSize = 0x1000; #region Constructor /// /// PdfContentResult /// Initializes a new instance of the FileStreamResult class. /// public PdfStreamResult(Stream fileStream) : base() { if (fileStream == null) { throw new ArgumentNullException("fileStream"); } this.FileStream = fileStream; } #endregion #region Overriden Methods /// /// WriteFile /// Writes the file to the response. (Overrides FileResult.WriteFile(HttpResponseBase).) protected override void WriteFile(HttpResponseBase response) { Stream outputStream = response.OutputStream; using (this.FileStream) { byte[] buffer = new byte[0x1000]; while (true) { int count = this.FileStream.Read(buffer, 0, 0x1000); if (count == 0) { return; } outputStream.Write(buffer, 0, count); } } } #endregion /// /// FileContents /// Gets the stream that will be sent to the response. public Stream FileStream { get; private set; } }
PdfPathResult
This implementation class return the Pdf document from a physical Pdf file on the server.
/// /// PdfPathResult /// Sends the contents of a file to the response. public class PdfPathResult : PdfResult { #region Constructor /// /// PdfContentResult /// Initializes a new instance of the FilePathResult class by using the specified file name and content type. /// public PdfPathResult(string fileName) : base() { if (string.IsNullOrEmpty(fileName)) { throw new ArgumentException("Value cannot be null or empty.", "fileName"); } this.FileName = fileName; } #endregion #region Overriden Methods /// /// WriteFile /// Writes the file to the response. (Overrides FileResult.WriteFile(HttpResponseBase).) protected override void WriteFile(HttpResponseBase response) { response.TransmitFile(this.FileName); } #endregion /// /// FileContents /// Gets the path of the file that is sent to the response. public string FileName { get; private set; } }
PdfContentResult
This implementation returns the Pdf document out of binary content of Pdf document.
/// /// PdfContentResult /// Sends the contents of a binary file to the response. /// public class PdfContentResult : PdfResult { #region Constructor /// /// PdfContentResult /// Initializes a new instance of the PdfContentResult class by using the specified file contents and content type. /// public PdfContentResult(byte[] fileContents) : base() { if (fileContents == null) { throw new ArgumentNullException("fileContents"); } this.FileContents = fileContents; } #endregion #region Overriden Methods /// /// WriteFile /// Writes the file content to the response. (Overrides FileResult.WriteFile(HttpResponseBase).) protected override void WriteFile(HttpResponseBase response) { response.OutputStream.Write(this.FileContents, 0, this.FileContents.Length); } #endregion /// /// FileContents /// The binary content to send to the response. public byte[] FileContents { get; private set; } }
Controller Extensions
I needed to write these extension to have the calling code look similar to ViewResult or other result types.
/// /// Defines extensions for controllers public static class ControllerExtensions { #region Pdf Extension method for Controller /// /// Pdf /// Creates a PdfContentResult object by using the file contents, content type, and the destination file name. /// The Controller /// The binary content to send to the response. /// The file name to use in the file-download dialog box that is displayed in the browser. /// The file-content result object. public static PdfResult Pdf(this Controller controller, byte[] fileContents, string fileDownloadName) { return new PdfContentResult(fileContents) { FileDownloadName = fileDownloadName }; } /// /// Pdf /// Creates a PdfStreamResult object using the Stream object, the content type, and the target file name. /// The Controller /// The stream to send to the response. /// The file name to use in the file-download dialog box that is displayed in the browser. /// The file-stream result object. public static PdfResult Pdf(this Controller controller, Stream fileStream, string fileDownloadName) { return new PdfStreamResult(fileStream) { FileDownloadName = fileDownloadName }; } /// /// Pdf /// Creates a PdfPathResult object by using the file name, the content type, and the file download name. /// The Controller /// The path of the file to send to the response. /// The file name to use in the file-download dialog box that is displayed in the browser. /// The file-stream result object. public static PdfResult Pdf(this Controller controller, string fileName, string fileDownloadName) { return new PdfPathResult(fileName) { FileDownloadName = fileDownloadName }; } #endregion }
Example
PdfStreamResult
public PdfResult DownloadPdf() { Stream fileStream = ...; //initialization code to initialize the stream with Pdf File. return this.Pdf(fileStream, "Sample.pdf"); }
PdfPathResult
public PdfResult DownloadPdf() { string filePath = ; //initialization code to initialize the string with Pdf File Path. return this.Pdf(filePath, "Sample.pdf"); }
PdfContentResult
public PdfResult DownloadPdfContent() { string filePath = ; //initialization code to initialize the string with Pdf File Path. byte[] pdfBytes = System.IO.File.ReadAllBytes(filePath); return this.Pdf(pdfBytes, "sample.pdf"); return pdfContent; }
awesome
excellent! thanks a lot
After over a year, still an excellent post – high quality code that works in MVC 5 to pull pdfs as a stream from an Azure cloud service.
Thanks!
Thank you, it works perfectly!
How can you open the PDF file directly in the browser with the binary content sent to the response?
It can be achieved by setting the response header Content-Disposition to inline.
Response.AppendHeader("Content-Disposition", "inline; filename=file.pdf");
The best place to write this line would be ExecuteResult method but my implementation is using the default, feel free to override and extend it.
More exactly you need to overwrite the FileResult class ExecuteResult method in your PdfResult class:
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException(“context”);
}
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = this.ContentType;
if (!string.IsNullOrEmpty(this.FileDownloadName))
{
//string headerValue = ContentDispositionUtil.GetHeaderValue(this.FileDownloadName);
//context.HttpContext.Response.AddHeader(“Content-Disposition”, headerValue);
context.HttpContext.Response.AppendHeader(“Content-Disposition”, “inline; filename=” + this.FileDownloadName);
}
this.WriteFile(response);
}
Thanks to all 🙂
blank page open when use response.addheader
could you please elaborate and post your sample code, I will try resolving, if there is any issue.
You can open the PDF in a new window or tab? I searched all over the internet, tested several methods, but did not get a way that works directly return to view, only using a second call of a button or link.
Opening in new window/tab is client functionality and any of the server side code will not have control on that. You can use target attribute on the anchor tag to invoke the Url in new window and that should work. Look at the previous comment to open the PDF in as inline window rather than downloading as file.
I get this issue (Output stream is not avaialble when the text writer is used) in PdfStreamResults.cs. The actual line that throws the error is Stream outputStream = response.OutputStream;
Have you seen this issue?
Thanks!
How are you calling the action method, Can post some code? Are you using Html.Action? If yes, try Url.Action to generate the link.