0

I am trying to display PDF on the browser with Sitecore MVC. The PDF is generating dynamically from back-end. Below are approaches I tried and also given outcome.

Approach 1:

    public ActionResult ViewPDF()
    {
        //The path is hard-coded here, but it is dynamic.  
        string pdfFilePath = "C:/Projects/7823986.pdf";
        byte[] bytes = System.IO.File.ReadAllBytes(pdfFilePath);

        Response.Clear();
        Response.ContentType = "application/pdf";
        Response.AddHeader("content-length", bytes.Length.ToString());
        Response.BinaryWrite(bytes);
        Response.End();

        return new EmptyResult();
    }

Approach 1 Outcome: Response.BinaryWrite(bytes) throws exception as "OutputStream is not available when a custom TextWriter is used."

Approach 2: Use Two Step process, use RedirectToAction in first step to redirect to another step action.

    public ActionResult ViewPDF()
    {
        return RedirectToAction("RenderPDF");
    }

    public ActionResult RenderPDF()
    {
        string pdfFilePath = "C:/Projects/7823986.pdf";
        byte[] bytes = System.IO.File.ReadAllBytes(pdfFilePath);

        Response.Clear();
        Response.ContentType = "application/pdf";
        Response.AddHeader("content-length", bytes.Length.ToString());
        Response.BinaryWrite(bytes);
        Response.End();

        return new EmptyResult();
    }

Approach 2 Outcome: It works fine in Sitecore "Standalone" instance. But it won't work on CD instances where Sitecore access is restricted: "You do not have permission to view this directory or page." As the default route pinged by the method performing the redirect is /sitecore/shell/api/sitecore/ControllerName/RenderPDF"

Approach 3: Create aspx layout (layout.aspx) and user control (ascx). And use logic to display

User control RenderPDF.ascx code below

    protected void Page_Load(object sender, EventArgs e)
    {
        try
        {
            if (!Page.IsPostBack)
            {
                // Render PDF on the browser.
                ViewPDFPage();
            }
        }
        catch (Exception ex)
        {
        }
    }

public ActionResult ViewPDF()
{
    //The path is hard-coded here, but it is dynamic.  
    string pdfFilePath = "C:/Projects/7823986.pdf";
    byte[] bytes = System.IO.File.ReadAllBytes(pdfFilePath);

    Response.Clear();
    Response.ContentType = "application/pdf";
    Response.AddHeader("content-length", bytes.Length.ToString());
    Response.BinaryWrite(bytes);
    Response.End();

    return new EmptyResult();
}

Approach 3 Outcome: It works fine including CD instance. The only problem is, It is not Sitecore MVC solution. It is a legacy aspx way.

Approach 4: Even I tried with FileContentResult as below.

    public FileResult ViewPDF()
    {
        string pdfFilePath = "C:/Projects/7823986.pdf";
        byte[] bytes = System.IO.File.ReadAllBytes(pdfFilePath);
        string fileName = "7823986.pdf";

       return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Pdf, fileName)
    }

Approach 4 Outcome: It did not work.

In all above approaches, Only Approach 3 works fine but in legacy way.

Could you please anyone suggest any solution which works with Sitecore MVC way.

4
  • Why are you instantiating ms as a new MemoryStream all the time, when you don't use it for anything?
    – Mark Cassidy
    Jul 14 '20 at 8:11
  • Answers here: stackoverflow.com/a/9549889/81631
    – Mark Cassidy
    Jul 14 '20 at 8:16
  • Mark, MemoryStream is not using here. I removed that step from question.
    – venkat
    Jul 14 '20 at 8:26
  • Just change the url from "/sitecore/shell/api/sitecore/ControllerName/RenderPDF" to some custom route and register that route. That's the reason why it doesn't work for you. Jul 14 '20 at 13:23
1

How to fix approach 2: Register a custom ASP.NET MVC route for your action. You need to create new processor:

public class RegisterCustomRoute
{
  public virtual void Process(PipelineArgs args)
  {
    RouteTable.Routes.MapRoute("CustomRoute", "some/route/{controller}/{action}/{id}");
  }
}

and add calling your processor to initialize pipeline:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
     <sitecore>
          <pipelines>
               <initialize>
                    <processor type="MyNamespace.RegisterCustomRoute, MyAssembly" patch:before="processor[@type='Sitecore.Mvc.Pipelines.Loader.InitializeRoutes, Sitecore.Mvc']" />
               </initialize>
          </pipelines>
     </sitecore>
</configuration>

Now that approach should work fine on both CM and CD servers.

0

You don't say how your ViewPDF() action method is getting called. If you're calling it from a Sitecore "controller rendering", then that's a problem because you're already in the Sitecore rendering pipeline when your action is called; the Sitecore rendering pipeline is already assembling a response.

I think you need to add a custom URL route to your application on app startup, routing to your action method. That way you completely bypass the Sitecore rendering pipeline, and your action method can return any kind of result you want.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.